如何让Java URI类停止使用文件?

问题描述 投票:0回答:2

我有一个小型的Java桌面应用程序,它读取一些图像文件并显示它们。问题是当我想在这些文件上执行一些操作(比如说,一个exiftool操作)时,它拒绝了,因为Java仍然在使用它们。

编辑:这只发生在Windows上,你不能在一个正在被Java处理(显示)的GIF动画文件(转换为URL对象)上写东西,但是在Ubuntu上,你可以编辑该文件的Metadata,系统不认为该文件正在被处理。

这是我的那部分代码。

我读取文件。

ImageInputStream iis = null;
ImageReader reader = null;
iis = ImageIO.createImageInputStream(
                    f);
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(iis);
reader = (ImageReader) imageReaders.next();

Path pathe = f.toPath();
String mimeType = Files.probeContentType(pathe);
ImageIcon icon = null;

这里是我处理图片的部分,调整图片大小,并制作JLabel来查看图片。

我之所以不只是用Java ImageIO来填充那个标签的图片,ImageIO只能显示动画GIF文件的第一帧。用这种方式将图像转换为URL,可以保持图像的动画效果(即使在调整大小后)。

ImageIcon icon = null;                    
Integer labelWidth = this.imageLabel.getWidth();
Integer labelHeight = this.imageLabel.getHeight();
URI img;
img = f.toURI();
URL umg = img.toURL();
icon = new ImageIcon(umg);

//some calculations for setting labelWidth and labelHeight here

icon.setImage(icon.getImage().getScaledInstance(labelWidth, labelHeight,
                                                                  Image.SCALE_DEFAULT));

最后,流被关闭。

this.imageLabel.setIcon(icon);
iis.close();
reader.dispose();

然后我试着通过一个进程执行一些exiftool命令,它成功地读取了图像Metadata。但是在更新数据的时候,如果图片是动画GIF的话,它会说 "Error renaming temporary file to C:Userspath..."。如果图像是非动画图像,JPEG,PNG或GIF,它可以读取和更新元数据,我想,没有问题发生。

当我取消图像显示部分的代码时,我可以在图像元数据上写入,而不会出现错误。如果我读取一个JPEG文件并显示它,仍然没有问题。如果我读取一个动画GIF文件并显示它(动画,是否保持文件连接打开?),在调试会话未关闭时,不能对这个文件进行任何修改,在我的程序中和cmd.exe上都不能。当我退出调试后,cmd.exe上的exiftool进程开始正常工作。

关闭ImageInputStream或ImageReader也没有用。

有没有办法让Java进程(如果文件是动画的,我用的是URI、URL类)在读取操作后释放文件?我提到的这些类是否有释放、关闭、关闭、杀死进程等方法。我需要读取动画图片并显示动画,并对其进行更新操作。

java windows swing file url
2个回答
1
投票

谢谢大家的评论,这是问题和解决方案。

首先我删除了代码中不必要的部分。ImageInputStream和ImageReader被用来检查图像验证和检测图像格式(必须对GIF文件使用不同的操作),我不需要了。

我仍然需要使用File->URI->URL转换来显示动画GIF。这是我的旧代码。

URI img = f.toURI();
URL umg = img.toURL();
icon = new ImageIcon(umg);  

这段代码保持了对文件的连接打开,并且阻止了其他编辑图像文件的进程。(只适用于动画GIF文件,在Windows系统上)

这是新的代码。

//these 2 lines are same
URI img = f.toURI();
URL umg = img.toURL();

InputStream is = umg.openStream();
byte[] byteChunk = new byte[4096]; 
int n;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((n = is.read(byteChunk)) > 0) 
{
baos.write(byteChunk, 0, n);
}
byte[] dd= baos.toByteArray();
icon = new ImageIcon(dd);
is.close();

通过这种方法,URI(image)是通过流读取的,ImageIcon是由image文件的字节数组创建的,而不是直接从URL中创建的,在该操作之后,InputStream被关闭,所以该文件的块被释放。(如果 "is.close() "行没有被执行,则文件仍在处理中,并对其他写操作进行阻塞)


0
投票

您是否尝试过用 Toolkit.createImage 方法?

试试这个方法吧。

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Main {
    private static class DrawPanel extends JPanel {
        private Image i = null;

        public void loadImage(final File f) {
            //Most important point: load image via Toolkit.
            //I think it must support GIF, JPEG and PNG.
            i = getToolkit().createImage(f.getAbsolutePath());
            repaint();
        }

        @Override
        protected void paintComponent(final Graphics g) {
            super.paintComponent(g);
            if (i != null) {
                //g.drawImage(i, 0, 0, this); //Draw full scale image.
                g.drawImage(i, 0, 0, getWidth(), getHeight(), this); //Draw scaled/streched image.
                //Supplying 'this' in place of ImageObserver argument to the drawImage method is also very important!
            }
        }
    }

    public static void main(final String[] args) {
        SwingUtilities.invokeLater(() -> {
            final DrawPanel imagePanel = new DrawPanel();
            imagePanel.setPreferredSize(new Dimension(500, 350));

            final JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            fileChooser.setMultiSelectionEnabled(true);

            final JButton load = new JButton("Load");
            load.addActionListener(e -> {
                if (fileChooser.showOpenDialog(imagePanel) == JFileChooser.APPROVE_OPTION)
                    imagePanel.loadImage(fileChooser.getSelectedFile());
            });

            final JPanel contents = new JPanel(new BorderLayout());
            contents.add(imagePanel, BorderLayout.CENTER);
            contents.add(load, BorderLayout.PAGE_END);

            final JFrame frame = new JFrame("Images");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(contents);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}
  1. 有几个 createImage 方法来选择。
  2. 我认为他们支持动画GIF,以及非动画JPG和PNG。我只对GIF进行了测试,但我过去也曾对JPG和PNG进行过测试。
  3. 我认为动画图像是完全加载到内存中的(所有帧)。所以你在读取图像后修改它应该没有问题。
  4. 获取 Toolkit 有关 Component 这将显示 Image. 我还认为 Toolkit.getDefaultToolkit 在某些情况下应该做的工作。然后用 drawImage 的方法 Graphics 对象来绘制图像。有几个 drawImage 方法来选择。例如,您可以使用为您快速缩放图像的方法(通过提供新的宽度和高度以及绘制位置)。重要提示:请确保你提供 drawImage 的组件,并将其渲染为 ImageObserver (注 Component implements ImageObserver).
© www.soinside.com 2019 - 2024. All rights reserved.