是否有可能说服 Swing 在 <img> jTable 单元格中使用标签缓存加载的图像,而不是在每次重绘时重新下载图像?



正如标题所述,我注意到每次重新启动组件时,都会重新下载使用<img>标签加载的远程托管图像。单元的内容看起来像<html><img src="http://www.example.com/image.png"></html>

通常,这并不是什么大的问题时间是重新粉刷组件),并且可能是任何差的文件服务器托管图像的请求垃圾邮件/bandwith问题。

是否有可能说服秋千首次仅在需要时下载图像,然后在重新粉刷时重新使用缓存的副本,而不是每次重新下载?

和澄清,我注意到这种行为的方式是:

  1. 在呈现大图时迅速重新粉刷JTable单元时,请注意清晰的性能下降。
  2. 在Netty文件服务器上托管所述图像文件,并看到控制台输出如下:该页面的页面和页面随着组件的重新粉刷而迅速出现

编辑,在问题上添加mcve:

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class Main {
    public static void main(String[] argv) {
        JFrame demoFrame = new JFrame("Table");
        JTable jTable = new JTable();
        demoFrame.getContentPane().add(new JScrollPane(jTable));
        DefaultTableModel dtm = new DefaultTableModel(new Object[]{"cache test"}, 5);
        jTable.setModel(dtm);
        jTable.setValueAt("<html><img src="https://i.imgur.com/zfa0mEn.png"></html>", 2, 0);
        demoFrame.pack();
        demoFrame.setVisible(true);
    }
}

也有点乏味和过度,也可以在某种形式的文件服务器中跟踪此处的文件请求,所以我已经决定反对。

我找到了一种解决问题的方法,可能不是最漂亮的,但它确实解决了性能问题和同一图像的不必要的垃圾邮件。

以下代码同时包含MCVE和TableModelListener实现,该实现将自动检测到何时将图像标签放入任何JTable单元格的内容中,并将局部下载到应用程序工作目录上创建的文件夹,以及然后替换标签的SRC属性指向该缓存文件。

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class Main {
    public static void main(String[] argv) {
        JFrame demoFrame = new JFrame("Table");
        JTable jTable = new JTable();
        demoFrame.getContentPane().add(new JScrollPane(jTable));
        DefaultTableModel dtm = new DefaultTableModel(new Object[]{"cache test"}, 5);
        jTable.setModel(dtm);
        jTable.getModel().addTableModelListener(new ImageCachingTableModelListener());
        jTable.setValueAt("<html><img src="https://i.imgur.com/zfa0mEn.png"></html>", 2, 0);
        demoFrame.pack();
        demoFrame.setVisible(true);
    }
    private static class ImageCachingTableModelListener implements TableModelListener {
        String regex = "<html>(?:[\s\S]*)<img(?:[\s\S]*)src="((?:[\S]+)\/([\S]+\.[A-Za-z0-9_-]+)(?:[\s\S]*))"(?:[\s\S]*)?>(?:[\s\S]*)<\/html>";
        Pattern pattern = Pattern.compile(regex);
        @Override
        public void tableChanged(TableModelEvent evt) {
            DefaultTableModel evtTableModel = (DefaultTableModel) evt.getSource();
            if (evt.getType() == TableModelEvent.UPDATE) {
                for (int i = evt.getFirstRow(); i <= evt.getLastRow(); i++) {
                    String content = (String) evtTableModel.getValueAt(i, evt.getColumn());
                    Matcher m = pattern.matcher(content);
                    if (m.find()) {
                        String imageSrc = m.group(1);
                        String imageFileName = m.group(2);
                        try (InputStream in = new URL(imageSrc).openStream()) {
                            File cacheDir = new File(System.getProperty("user.dir") + File.separator + "imgcache");
                            if (!cacheDir.exists()) {
                                cacheDir.mkdirs();
                            }
                            Path cachedFilePath = Paths.get(cacheDir.getCanonicalPath() + File.separator + imageFileName);
                            Files.copy(in, cachedFilePath, StandardCopyOption.REPLACE_EXISTING);
                            evtTableModel.setValueAt(content.replace(imageSrc, "file:" + cachedFilePath.toString()), i, evt.getColumn());
                        } catch (MalformedURLException ex) {
                            ex.printStackTrace();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

感谢您的帮助,希望这对别人有用。谢谢Artemis在代码的正则一部分方面提供帮助。

最新更新