You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JTextPane加载含HTML内容的file:// URI图片失败问题

图片缓存无法在JTextPane的HTML img标签中加载

我在应用目录中实现了图片缓存功能,已完成图片下载与存在性检测的代码开发,但无法在HTML的img标签src属性中使用本地缓存的图片。

我参考过两篇相关文章,但第一篇中提问者采用的图片源设置方法不适用于我的场景;第二篇文章指出file://协议应无需额外配置即可使用,但在我的场景中无效。

我尝试了替换正斜杠与反斜杠、将空格替换为%20、使用file://与file:///前缀等多种方式,均未解决问题。我可以通过ImageIO读取本地图片,但JTextPane的HTML渲染器无法找到这些图片,仅显示错误占位符。

示例图片路径:

file:///D:/My%20Documents/NetBeansProjects/XMessenger/cache/http%3A%2F%2Fpopov654.pp.ru%2Fcopybox%2Fphoto.jpg

相关代码:

private int last_width = 0;
private HashMap<String, Image> imgCache = new HashMap<String, Image>();

private void createTestMessageContent(final JComponent parent) {

    String html = "Some <i>text</i><br>And more,<br>And more...<br>And more...<br>And more...<br>And more...<br>And more...<br>" + 
            "<img src=\"http://popov654.pp.ru/copybox/photo.jpg\" width=\"390\" height=\"260\">" +
            "<img src=\"http://popov654.pp.ru/copybox/DSCN2155.jpg\" width=\"390\" height=\"260\">" +
            "<img src=\"http://popov654.pp.ru/copybox/DSCN2157.jpg\" width=\"390\" height=\"260\">";
    html = "<div style=\"padding: 0px; font-size: 14px; font-family: Tahoma\">" + html + "</div>";

    final String orig_html = html;

    Matcher m = Pattern.compile("((<img src=\"[^\"]+\")( width=\"\\d+\")?( height=\"\\d+\")?([^>]*>)\\s*)+").matcher(orig_html);
    while (m.find()) {
        String str = m.group();
        Matcher m2 = Pattern.compile("src=\"([^\"]+)\"").matcher(str);
        while (m2.find()) {
            File f = new File("cache");
            if (!f.exists()) {
                f.mkdir();
            }
            String src = m2.group(1);
            String cached_name = src.replaceAll("/", "%2F").replaceAll(":", "%3A");
            File img = new File("cache/" + cached_name);
            System.err.println(img.getAbsolutePath());
            if (!img.exists()) {
                InputStream in = null;
                BufferedInputStream b_in = null;
                FileOutputStream fileOutputStream = null;
                try {
                    in = new URL(src).openStream();
                    b_in = new BufferedInputStream(in);
                    fileOutputStream = new FileOutputStream("cache/" + cached_name);
                    byte dataBuffer[] = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
                        fileOutputStream.write(dataBuffer, 0, bytesRead);
                    }
                    //Files.copy(in, Paths.get("cache/" + cached_name), StandardCopyOption.REPLACE_EXISTING);
                } catch (IOException ex) {
                    ex.printStackTrace();
                } finally {
                    try {
                        in.close();
                        b_in.close();
                        fileOutputStream.close();
                    } catch (IOException ex) {}
                }
            } else {
                html = html.replaceFirst("src=\"" + src + "\"", "src=\"file:///" + img.getAbsolutePath().replaceAll("\\\\", "/").replaceAll(" ", "%20") + "\"");
            }
        }
    }

    final String cached_html = html;

    html = html.replaceAll("(<img src=\"[^\"]+\")( width=\"\\d+\")?( height=\"\\d+\")?([^>]*>)", "<a href=\"#\" style=\"display: inline-block; margin-right: 10px\">$1 width=\"390\" height=\"260\" hspace=\"2\" vspace=\"8\" border=\"0\"$4</a>");
    System.out.println(html);

    msgContent.setText(html);
    addHyperlinkListener(msgContent);


    //msgContent.setMinimumSize(new Dimension(msgContent.getWidth(), msgContent.getPreferredSize().height + 20));
    //setMinimumSize(new Dimension(getWidth(), msgContent.getPreferredSize().height + 160));
    setPreferredSize(new Dimension(getWidth(), msgContent.getPreferredSize().height + 148));
    
    initImages();

    updateSize();

    content2.addComponentListener(new java.awt.event.ComponentListener() {

        @Override
        public void componentResized(ComponentEvent e) {
            int new_width = ((JPanel)e.getSource()).getWidth();
            if (new_width != last_width) updateImages(cached_html);
            last_width = new_width;
        }

        @Override
        public void componentMoved(ComponentEvent e) {}

        @Override
        public void componentShown(ComponentEvent e) {}

        @Override
        public void componentHidden(ComponentEvent e) {}

    });
}

private void initImages() {
    HTMLDocument doc = (HTMLDocument) msgContent.getStyledDocument();
    Element[] imgs = getElementsByTagName(HTML.Tag.IMG, doc);
    for (Element img: imgs) {
        Element a = img.getParentElement();
        try {
            String src = (String) img.getAttributes().getAttribute(HTML.Attribute.SRC);
            System.out.println("src=" + src);
            System.out.println();

            Image image = null;
            if (!src.startsWith("http://") && !src.startsWith("https://")) {
                String path = src;
                if (src.startsWith("file:///")) {
                    path = path.substring(8).replaceAll("%20", " ");
                }
                image = ImageIO.read(new File(path));
            } else {
                image = ImageIO.read(new URL(src));
            }
            imgCache.put(src, image);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

private void updateImages(String orig_html) {
    int width = XMessengerApp.getMainWindow().getMessagePaneWidth() - 108;

    double default_ratio = 16f / 9;

    int img_width = (int) (width * 0.65);
    int img_height = (int) ((double)img_width / default_ratio);
    int margin = (int) (width * 0.005);

    String html = orig_html;
    Matcher m = Pattern.compile("((<img src=\"[^\"]+\")( width=\"\\d+\")?( height=\"\\d+\")?([^>]*>)\\s*)+").matcher(orig_html);
    while (m.find()) {
        String str = m.group();
        String str_new = str;
        String[] img_strs = str.split(">\\s*<");
        if (img_strs.length > 1) {
            System.err.println("Image series found of " + img_strs.length + " images");
        }
        if (img_strs.length > 1) {
            img_width = (int)((width - margin * img_strs.length) / img_strs.length * 0.95);
            img_height = (int)((double)img_width / default_ratio);
        }
        for (int i = 0; i < img_strs.length; i++) {
            if (i > 0) img_strs[i] = "<" + img_strs[i];
            if (i < img_strs.length-1) img_strs[i] = img_strs[i] + ">";

            Matcher m2 = Pattern.compile("src=\"([^\"]+)\"").matcher(img_strs[i]);
            m2.find();
            String src = m2.group(1);

            double ratio = default_ratio;
            if (imgCache.containsKey(src)) {
                Image img = imgCache.get(src);
                ratio = (double) img.getWidth(null) / img.getHeight(null);
                //System.out.println("Aspect ratio: " + ratio);
            }
            if (img_strs.length == 1) {
                img_height = (int)((double)img_width / ratio);
            } else {
                img_width = (int)((double)img_height * ratio);
            }
            //System.out.println("Src: " + src);

            String replace = img_strs[i].replaceAll("(<img src=\"[^\"]+\")( width=\"\\d+\")?( height=\"\\d+\")?([^>]*>)", "<a href=\"#\" style=\"display: inline-block; margin-right: " + margin + "px\">$1 width=\"" + img_width + "\" height=\"" + img_height + "\" hspace=\"" + margin + "\"vspace=\"8\" border=\"0\"$4</a>");
            str_new = str_new.replaceFirst(img_strs[i], replace);
        }
        if (img_strs.length > 1) {
            str_new = "<div style=\"float: left; margin-left: -10px\">" + str_new + "</div>";
        }
        html = html.replaceFirst(str, str_new);
            
    }


    msgContent.setText(html);

}

private void updateSize() {
    try {
        Dimension d = msgContent.getPreferredSize();
        Rectangle r = msgContent.modelToView(msgContent.getDocument().getLength());
        d.height = r.y + r.height;
        msgContent.setPreferredSize(d);
        Dimension d2 = content2.getPreferredSize();
        d2.height = r.y + r.height + 200;
        content2.setPreferredSize(d2);
        JFrame topFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
        topFrame.getContentPane().validate();
    } catch (Exception ex) {}
}

内容的提问来源于stack exchange,提问作者Alex654

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 09:55:14