Displaying Attachment PDF with Frontend Javascript

背景

笔者最近在参加某校在线平台前端的开发,收到了这样的要求:

将服务器传输过来的文件尽可能(图片/PDF)在网页中就显示下来,这样就不用下载了!

笔者劈里啪啦敲下了如下代码(由于渲染问题,美元符号已替换为人民币符号!!!):

var xhr = new XMLHttpRequest();

¥.ajax({
    url: 'download_link',
    type: 'get',
    data: {},
    xhr: function () {
        return xhr;
    },
    success: function (r) {
        const params = new URLSearchParams(xhr.responseURL);
        const para1 = params.get('filename');
        console.log("Returned url:"+xhr.responseURL);
        console.log(para1);

        if (para1 == null) {
            ¥('#fast_load').text('');
            return;
        }

        if (para1.endsWith(".png") || para1.endsWith(".jpg") || para1.endsWith(".gif") || para1.endsWith(".bmp")) {
            ¥('#fast_load').html(`用户上传的图片(为正常显示,已缩放。请点击下载按钮下载原图。)<br/><br/><img src="¥{xhr.responseURL}" style="max-width:600px;width:100%"/>`);
            return;
        }

        if(para1.endsWith(".pdf")){
            ¥('#fast_load').html(`用户上传的PDF<br/><br/><object class="pdf" data="¥{xhr.responseURL}" width="100%" height="1500"></object>`);
            return;
        }

        ¥('#fast_load').text(`用户上传了文件:¥{para1},请按下载按钮下载。请注意:本网站不对用户上传内容进行检查,文件有可能包含恶意内容。`);
    }
});

上述代码可以正确分辨并显示各种常用类型的图片文件,然而pdf文件则会直接下载,不会显示在object中,令人迷惑。

问题分析

原来,服务器返回的是Content-Type是application/octet-stream,而又是以attachment的形式送给我们的,这时候无论iframe embed 还是 object都不会尝试在浏览器中渲染该pdf,而是直接下载。我们无法改变服务器发送的数据类型,于是我们尝试在客户端层面解决问题。

解决思路

Blob类型接受response然后本地改变Content-Type到application/pdf,再用本地数据渲染。

问题解决

首先,我们需要以Blob类型处理response,然而据说jquery处理blob类型有许多问题(事实上笔者也确实碰到了问题),所以我们换用普通的Ajax:

var req = new XMLHttpRequest();
req.open("GET", "...", true);
req.responseType = "blob";

req.onload = function (event) {
    var blob=req.response;
    //...
};

req.send();

接下来,我们发现Blob的类型还是octet-stream,直接放入object中仍然失败,所以我们需要修改其type。然而Blob是immutable的,我们无法直接修改其参数。根据本文,我们这么修改blob的类型:

//hacky solution!!
blob = blob.slice(0, blob.size, "application/pdf")

最后我们使用非常强大的URL工具创建本地URL:

if(para1.endsWith(".pdf")){
    blob=blob.slice(0,blob.size,"application/pdf");
    const url=URL.createObjectURL(blob);
    ¥('#fast_load').html(`用户上传的PDF<br/><br/><object class="pdf" data="¥{url}" width="100%" height="1500"></object>`);
    return;
}

该URL会随着document卸载而卸载,本场景下不用手动卸载。

(欸,我的代码中的美元都到哪去了?)

重点

  • 如何获取服务器上的Blob数据
  • 如何修改Blob的类型
  • 如何用Blob创建本地URL

版权声明:
作者:XGN
链接:https://blog.hellholestudios.top/archives/1568
来源:Hell Hole Studios Blog
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录