【问题标题】:How to make Java URI class to stop using a file如何使 Java URI 类停止使用文件
【发布时间】:2020-08-01 06:41:28
【问题描述】:

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

编辑:这仅在 Windows 上发生,您不能在 Java 正在处理(显示)的动画 GIF 文件(转换为 URL 对象)上写入,但在 Ubuntu 上,您可以编辑该文件元数据、系统不认为该文件正在处理中。

这是我的那部分代码。

我读了文件;

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 命令,它成功读取了图像元数据。但是在更新数据时,如果图像是动画 GIF,它会显示“将临时文件重命名为 C:/Users/path... 时出错”。 如果图像是非动画图像、JPEG、PNG 或 GIF,则不会出现问题,我猜它可以读取和更新元数据。

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

关闭 ImageInputStream 或 ImageReader 没有帮助。

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

【问题讨论】:

  • 你在windows上吗?那么我认为是windows内部文件访问机制。就像你在outlook中发送一个文件作为附件一样,windows保持访问直到outlook关闭(不管邮件是否发送)
  • 我强烈建议使用try-with-resources blocks 来确保关闭/处置物品。
  • 嗨@bish,是的,我在 Windows 10 上,在你发表评论后,我尝试在 Ubuntu 上执行我的代码,它没有问题。因此,在 Windows 系统上,如果将 GIF 文件作为 URL 对象读取并显示,Java 会锁定它,但会忽略 JPEG 文件。在 Ubuntu 上,它根本不关心,你可以对正在处理的文件做任何你想做的事情。

标签: java windows swing file url


【解决方案1】:

你试过用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. 获取与Component 相关的Toolkit,这将显示Image。我认为Toolkit.getDefaultToolkit 也应该在某些情况下完成这项工作。然后使用Graphics对象的drawImage方法来绘制图像。有几种drawImage 方法可供选择。例如,您可以使用为您动态缩放图像的那个(通过提供新的宽度和高度以及绘制位置)。 重要提示:确保为drawImage 提供将其呈现为ImageObserver 的组件(注意Component implements ImageObserver)。

【讨论】:

  • 感谢您的评论,在我尝试您的建议之前,我已经为我的案例创建了一个解决方案。
  • 如果您自己解决了自己的问题,请在此处发布答案,以便其他人知道您已经解决了。并且还发布它,因为我很想知道如何解决这个问题(我相信其他人也会这样做)。然后不要忘记在最短的自我接受时间过去后接受你自己的答案。
  • 我一找到解决方案,就回到这里回答我的问题,只是先回复了您的评论。再次感谢。
【解决方案2】:

感谢cmets,这是问题和解决方案。

首先,我从代码中删除了不必要的部分。 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 是从图像文件的字节数组而不是直接从 URL 创建的,并且在该操作之后 InputStream 被关闭,因此该文件上的块被释放。 (如果“is.close()”行没有被执行,文件仍在处理中并阻塞其他写操作)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多