Как заставить Java URI-класс перестать использовать файл - PullRequest
0 голосов
/ 18 апреля 2020

У меня небольшое Java настольное приложение, оно читает некоторые файлы изображений и отображает их. Проблема в том, что когда я хочу выполнить что-то (скажем, операцию exiftool) с этими файлами, он отказывается, потому что Java все еще использует их.

edit: это происходит только на Windows, вы невозможно записать анимированный GIF-файл (преобразованный в объект URL), который обрабатывается (отображается) с помощью Java, но в 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 через процесс успешно считывают метаданные изображения. Но при обновлении данных появляется сообщение «Ошибка переименования временного файла в C: / Users / path ...», если изображение представляет собой анимированный GIF. Никаких проблем не возникает, если изображение не анимированное, JPEG, PNG или GIF, оно может считывать и обновлять метаданные, я полагаю.

Когда я отменяю изображение, отображающее часть кода, я могу писать на метаданные изображения без ошибок. Если я читаю файл JPEG и отображаю его, проблем нет. Если я читаю анимированный GIF и отображаю его (анимированный, поддерживает ли он соединение с файлом открытым?), Я не смогу изменить этот файл, ни в моей программе, ни в cmd.exe, пока сеанс отладки не закрыт. После завершения отладки процесс exiftool на cmd.exe начинает работать нормально.

Закрытие ImageInputStream или ImageReader не помогло.

Есть ли способ сделать процесс Java (если файл анимированные, я использую URI, классы URL) освободить файл после операции чтения? Есть ли в этих классах, которые я упоминал, методы для освобождения, закрытия, выключения, уничтожения и т. Д. c. Мне нужно прочитать анимированные изображения, отобразить их анимированные и выполнить над ними операции обновления.

Ответы [ 2 ]

1 голос
/ 19 апреля 2020

спасибо за комментарии, вот проблема и решение.

Прежде всего я удалил ненужные части из кода. 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 (изображение) читается через поток, а ImageIcon создается из изображения байтовый массив файла не напрямую из URL, и после этой операции InputStream закрывается, поэтому блок в этом файле освобождается. (Если строка «is.close ()» не выполнена, файл все еще обрабатывается и блокируется для других операций записи)

0 голосов
/ 19 апреля 2020

Вы пытались загрузить изображение с помощью Toolkit.createImage метода?

Дайте этому go:

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).
...