需求
在云证件web端支付的模块中,需要在支付成功后,将之前操作中上传到服务器中的图片放置于pdf中,并于浏览器中直接打开。具体实现效果如下:
着手思路
因为之前在进销存的项目里我有看到这种类似的操作,于是参照进销存的方法去实现,然而发现实际效果是将这个文件给下载下来,而不是在浏览器直接打开。然后开始查看接口返回的数据,发现在进销存的项目中,返回的数据是文件流,而在云证件中返回的是url地址。
在进销存中,调用封装好的buffer方法将返回数据转换为文件流,generateFIle函数将文件流转成blob对象,然后通过window.URL.createObjectURL()创建URL的File对象,将其赋予给新生成的a标签的href属性,然后通过click方法,实现在浏览器中直接打开图片。具体方法如下:
// 打印出库单
printLabel() {
let that = this;
that.$axios.buffer("/george-advance/inv/stock/print",{ id: that.queryInfo.shipId,type:"STOCK_OUT" },false)
.then(res => {
that.$common.generateFIle(res);
});
},
/**
* 二进制文件下载
* @param url string api接口
* @param param post object 参数
* @param showcode bool 是否提示返回信息bool
* @param layout bool 防呆层,避免连续触发
*/
buffer(url, param, showcode = false, layout = false) {
let loading = null;
let timer = null;
if (layout) {
loading = Loading.service({
lock: true,
text: "Loading",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
});
timer = setTimeout(() => {
layout = false;
loading.close();
}, jxConfig.timeout);
}
return new Promise((resolve, reject) => {
axios({
method: "post",
url: url,
data: param,
responseType: "arraybuffer",
cancelToken: new CancelToken(c => {
cancel = c;
})
}).then(res => {
if (res.headers["content-type"] === "application/json;charset=UTF-8") {
let uint8 = new Uint8Array(res.data); // 提取uint8Array
let resToString = JSON.parse(
decodeURIComponent(escape(String.fromCharCode(...uint8)))
); // 解决乱码
if (layout) {
loading.close();
clearTimeout(timer);
}
if (resToString.code === "200" && showcode) {
Message({
showClose: true,
message: resToString.message,
type: "success"
});
} else if (resToString.code !== "200") {
Message({
showClose: true,
message: resToString.message,
type: "warning"
});
}
return false;
} else {
resolve(res);
}
});
}).catch(function(reason) {
console.log("catch:", reason);
});
}
/**
* 文件流转成文件
* @param res 请求响应数据
*/
generateFIle(res) {
let type = res.headers["content-type"];
let fileName = this.getTime("YYYYMMDDHHMMss") + ".pdf";
let blob = new Blob([res.data], { type: type });
//判断浏览器非ie和edge
var userAgent = window.navigator.userAgent;
if(userAgent.indexOf("Edge")==-1 && userAgent.indexOf("Trident")==-1){
//浏览器直接打开
var url = window.URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.target = '_blank';
document.body.appendChild(link);
link.click();
return false;
}
if (typeof window.navigator.msSaveBlob !== "undefined") {
/*
* IE workaround for "HTML7007: One or more blob URLs were revoked by closing
* the blob for which they were created. These URLs will no longer resolve as
* the data backing the URL has been freed."
*/
window.navigator.msSaveBlob(blob, fileName);
} else {
let URL = window.URL || window.webkitURL;
let objectUrl = URL.createObjectURL(blob);
if (fileName) {
var a = document.createElement("a");
// safari doesn't support this yet
if (typeof a.download === "undefined") {
window.location = objectUrl;
} else {
a.href = objectUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
}
} else {
window.location = objectUrl;
}
}
}
遭遇问题
尝试改变请求返回类型无果后,就想着像处理图片一样,将url转化为canvas对象,然后通过canvas.toDataURL()将其转化为dataUrl,再new出一个blob对象。而将pdf的url转化为canvas是通过vue-pdf完成的,并且在后面的实现过程中会发现转化出来的base64无法还原成原图,并且这种方法还有一种弊端就是会造成图片画质降低。
然后回归到一开始的方法,在发送GET请求的时候将responeseType更改为"blob",于是开始用公共方法里的get方法尝试,调返回的url地址,发现会有跨域问题,并且就算将responseType更改了也无法转化为blob对象。这里也有个思维误区,觉得一定要使用封装好的方法去掉请求,于是一直没有去创建一个XML去调返回的url,将其转化为文件流,最后通过这种方法实现了得到的url转化为blob对象。具体代码如下:
downLoadPdf() {
let that = this;
that.$axios.post("/eastman/od/orderToPdf", { orderId: that.datas.orderId }).then(res => {
if (res.data.code === "200") {
return res.data.data.ossUrl
}
}).then(ossUrl=>{
that.saveAs(ossUrl);
})
;
},
saveAs(ossUrl) {
let that = this;
var oReq = new XMLHttpRequest();
// Configure XMLHttpRequest
oReq.open("GET", ossUrl, true);
// Important to use the blob response type
oReq.responseType = "blob";
// When the file request finishes
// Is up to you, the configuration for error events etc.
oReq.onload = function() {
// Once the file is downloaded, open a new window with the PDF
// Remember to allow the POP-UPS in your browser
var file = new Blob([oReq.response], {
type: 'application/pdf'
});
that.$common.generateFIle(file);
};
oReq.send();
},
generateFIle(res) {
let fileName = this.getTime("YYYYMMDDHHMMss") + ".pdf";
let blob = res;
//判断浏览器非ie和edge
var userAgent = window.navigator.userAgent;
if(userAgent.indexOf("Edge")==-1 && userAgent.indexOf("Trident")==-1){
//浏览器直接打开
var url = window.URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.target = '_blank';
document.body.appendChild(link);
link.click();
return false;
}
if (typeof window.navigator.msSaveBlob !== "undefined") {
window.navigator.msSaveBlob(blob, fileName);
} else {
let URL = window.URL || window.webkitURL;
let objectUrl = URL.createObjectURL(blob);
if (fileName) {
var a = document.createElement("a");
if (typeof a.download === "undefined") {
window.location = objectUrl;
} else {
a.href = objectUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
}
} else {
window.location = objectUrl;
}
}
}
反思
这个问题其实在解决之后会想会觉得其实也没有多少步骤,然而如果走错方向的话,也是到不了终点的,这也是为什么说方向正确比努力更重要。
一开始参照进销存项目的想法并没有错,只是在参照的时候也不能生搬硬套,要了解其中的原理,作出调整,才能够在不同的项目中根据条件实现效果。
对于一些开发上的问题还是因为经验尚浅而碰壁,在请教他人之前,自己先思考尝试,请教完之后,也要先按照方法实现一遍,如果有什么顾虑或者想法的话也要及时提出,不然沟通成本会很高。
本文由 纸鸢's 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2019/10/22 11:43