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

时间:2019-02-22 06:52:13

标签: java swing

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

通常,这不是什么大问题,尽管具有足够大的图像和足够频繁地自我重绘的组件,这都可能成为应用程序的性能问题(因为在重新下载图像时,它会冻结。组件重新粉刷的时间),并且可能会向请求托管该图像的较差文件服务器发送垃圾邮件/带宽问题。

是否可以说服Swing在第一次需要图像时才下载它,然后在重新绘制时重新使用缓存的副本,而不是每次都重新下载它?

为了澄清,我注意到此行为的方式是:

  1. 在呈现大图像的同时快速重绘jTable单元时,可以避免明显的性能下降。
  2. 将所述图像文件托管在Netty文件服务器上,并查看控制台输出,如下所示: Pages and pages of this, and more appearing rapidly as the component is repainted

编辑,向问题中添加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);
    }

}

还要放入某种文件服务器来跟踪这里的文件请求会有点乏味和过度,所以我决定反对。

1 个答案:

答案 0 :(得分:2)

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

以下代码包含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在代码正则表达式部分的帮助。