前言
最近在写uniapp,第一次写。。。项目从pc端写个对应的app端。遇到下载功能,想到这功能比较常见,而且社区很多人遇到这个问题,就多写点,写通用点,后面再遇到也可以用,分享给大家,记得点赞哦!
自行选择合适方法即可,支持:
1.uniapp h5 端下载。
2.unapp 开发的页面嵌入到uniapp webview 中,或者 h5 plus 中
3.uniapp 非h5 端下载 (支持app,其他端自行测试)
4. axios + responseType: "blob" 的下载
5. uni.request + responseType: 'arraybuffer' + window.URL.createObjectURL(返回值) + a标签download方式 下载
代码如下:
/**
* 文件下载相关支持axios、uniapp各端
* author: yangfeng
* date: 2023/08/10
* /
/**
* pc端axios方式下载
* 方式一:使用 uni.request + responseType: 'arraybuffer' + window.URL.createObjectURL(返回值) + a标签download方式
* 方式二:使用 axios + responseType: "blob"
* @param {*} arrayBufferOrBlob arraybuffer 或者 blob。若是axios则设置为 responseType: 'blob',若使用 uni.request 则 responseType: 'arraybuffer'
* @param {*} fileName 需要保存的文件名称
*/
export function pc_axios_download(arrayBufferOrBlob, fileName) {
const blob = new Blob([arrayBufferOrBlob], {
type: "application/vnd.ms-execl",
});
if (typeof window.navigator.msSaveBlob !== "undefined") {
// 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件
window.navigator.msSaveBlob(blob, decodeURI(fileName));
} else {
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(blob);
// 创建a标签,用于跳转至下载链接
const tempLink = document.createElement("a");
tempLink.style.display = "none";
tempLink.href = blobURL;
tempLink.setAttribute("download", decodeURI(fileName));
// 兼容:某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === "undefined") {
tempLink.setAttribute("target", "_blank");
}
// 挂载a标签
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL);
}
}
/**
* h5方式下载临时路径的文件 - 创建a标签,download的方式
* @param {*} blobURL 附件的临时路径
* @param {*} fileName 下载后的该文件名称
*/
export function h5_download(blobURL, fileName) {
// 创建a标签,用于跳转至下载链接
const tempLink = document.createElement("a");
tempLink.style.display = "none";
tempLink.href = blobURL;
tempLink.setAttribute("download", decodeURI(fileName));
// 兼容:某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === "undefined") {
tempLink.setAttribute("target", "_blank");
}
// 挂载a标签
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL);
}
function checkDownload() {
plus.io.requestFileSystem(plus.io.PUBLIC_DOWNLOADS, function (fs) {
var directoryReader = fs.root.createReader();
directoryReader.readEntries(
function (entries) {
var i;
for (i = 0; i < entries.length; i++) {
console.log(entries[i].name);
entries[i].name = i;
}
},
function (e) {
console.log("Read entries failed: " + e.message);
}
);
});
}
// h5Plus 下载 - plus.downloader.createDownload api
export function h5Plus_download(url, options = {}) {
return new Promise((resolve, reject) => {
let dtask = plus.downloader.createDownload(
url,
options,
function (d, status) {
if (status == 200) {
//下载成功,d.filename是文件在保存在本地的相对路径,使用下面的API可转为平台绝对路径
let fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename);
console.log(fileSaveUrl, "平台绝对路径");
uni.showModal({
title: "下载成功,是否打开文件?",
content: "文件目录存储目录为:" + fileSaveUrl,
success: function (res) {
if (res.confirm) {
// console.log('用户点击确定');
//选择软件打开文件
plus.runtime.openFile(d.filename);
} else if (res.cancel) {
// console.log('用户点击取消');
}
},
});
resolve(d);
// checkDownload()
// console.log("Download success: " + JSON.stringify(d));
// 保存图片、视频到相册中
// plus.gallery.save(d.filename, function () {
// resolve(d);
// console.log( "保存图片到相册成功" );
// // 打开文件
// plus.runtime.openFile(d.filename, (err) => {
// console.log(err);
// });
// },function(){
// reject();
// console.log( "保存图片到相册失败" );
// });
// let arr = fileSaveUrl.split('/')
// let dirPath = arr.slice(0, arr.length-1).join('/') // 当前文件所在目录
// console.log(getApp().globalData, dirPath, 666)
// // 通过URL参数获取目录对象或文件对象
// plus.io.resolveLocalFileSystemURL(dirPath, function( entry ) {
// console.log(entry, 44)
// // 打开文件目录
// entry.getDirectory('',{create:true,exclusive:false},function( dir ){
// console.log("Directory Entry Name: " + dir.name);
// }, function (e) {
// console.error(e, 444)
// })
// }, function ( e ) {
// alert( "Resolve file URL failed: " + e.message );
// } );
} else {
reject();
console.log("Download failed: " + status);
}
}
);
dtask.start();
});
}
// 非h5下载
export function noH5_download(tempFilePath) {
return new Promise((resolve, reject) => {
uni.saveFile({
tempFilePath: tempFilePath,
success: function (red) {
let savedFilePath = red.savedFilePath; // 相对路径
let fileSaveUrl = plus.io.convertLocalFileSystemURL(savedFilePath);
console.log(fileSaveUrl, "平台绝对路径");
uni.showModal({
title: "提示",
content: "文件已保存:" + fileSaveUrl,
cancelText: "我知道了",
confirmText: "打开文件",
success: function (resMdel) {
if (resMdel.confirm) {
uni.openDocument({
filePath: savedFilePath,
success: (sus) => {
console.log("成功打开");
},
fail(){
uni.showToast({
icon: 'none',
title: '暂不支持打开此格式文件'
})
}
});
}
},
});
resolve(red);
},
fail(e) {
console.error(e);
reject(e);
},
});
});
}
/**
*下载文件资源到本地,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。注意:1.h5可能存在跨域问题 2.只支持get方式下载
* @param {*} url 下载的完整路径
* @param {*} fileName 下载后的该文件名称
* @param {*} config uni.downloadFile的其他配置,如可以在header 添加token参数
* @returns Promise<any>
*/
export function uni_DownloadFile(url, fileName, config = {}) {
return new Promise((resolve, reject) => {
uni.downloadFile({
url: url, //仅为示例,并非真实的资源
// header: {
// "x-token": useUserStore().token,
// },
...config,
success: (res) => {
let tempFilePath = res.tempFilePath;
// console.log(res, 4444);
// h5 方式下载
// #ifdef H5
let isPlus = null;
try {
isPlus = plus;
} catch (e) {}
if (isPlus) {
// 是否支持h5 plus,针对当前页面被uniapp webview 引入 或者被包裹在 h5+ 壳子里,使用 h5plus 方式下载
// 采用a标签download方式下载会失败,因此需要针对处理
// 虽然会弹出下载框,但是下载之后你发现打开时失败,文件路径错误;这是因为webview中下载文件出现套娃现象
console.log("当前是h5 嵌入到 h5+ 环境");
h5Plus_download(url, { filename: "_downloads/" + fileName })
.then((result) => {
resolve(result);
})
.catch(() => {
reject();
});
} else {
console.log("当前是h5环境下载");
// window.location.href= tempFilePath
h5_download(tempFilePath, fileName);
resolve(res);
}
// #endif
// 非 h5 方式下载 - 需要保存文件到本地,如app里面的下载
// #ifndef H5
console.log("当前非h5环境下载");
noH5_download(tempFilePath)
.then((result) => {
resolve(result);
})
.catch(() => {
reject();
});
// #endif
},
fail(e) {
console.error(e);
reject(e);
},
});
});
}
调用方式:
1.uni 方式下载,兼容多端,如h5、嵌入到webview的h5、嵌入到h5 plus 环境的h5、uniapp vueapp等,其他的自行测试
/**
* 根据下载路径下载文件
* @param {*} url 下载路径,绝对路径,必选
* @param {*} fileName 下载文件名称,必选
* @returns
*/
export const downloadFileFromUrl = (url, fileName) => {
// uni方式 下载
return uni_DownloadFile(url,fileName,{
header: {
"x-token": useUserStore().token,
},
})
// pc、h5 方式下载
// return service
// .request({
// url: url,
// method: "get",
// // responseType: "blob", // axios 等pc端通用下载
// responseType: 'arraybuffer', // uni.request方式下载 文档里说这里只有text、arraybuffer,因此不能使用blob
// headers: {
// "x-token": useUserStore().token,
// },
// }, false)
// .then((res) => {
// // console.log(res,res.data, 111)
// if(res.status !== 200) return Promise.reject(res.errMsg || res.message || '下载失败')
// pc_axios_download(
// res.data,
// fileName ||
// (res.headers || res.header)["content-disposition"].replace(
// /.+filename=(.+)$/,
// "$1"
// ) ||
// url.split("/").pop()
// );
// })
// .catch((err) => {
// console.log(err);
// return Promise.reject(err);
// });
};
2. uni.request 方式在h5端下载
/**
* 根据下载路径下载文件
* @param {*} url 下载路径,绝对路径,必选
* @param {*} fileName 下载文件名称,必选
* @returns
*/
export const downloadFileFromUrl = (url, fileName) => {
// uni方式 下载
// return uni_DownloadFile(url,fileName,{
// header: {
// "x-token": useUserStore().token,
// },
// })
// pc、h5 方式下载
return service
.request({
url: url,
method: "get",
// responseType: "blob", // axios 等pc端通用下载
responseType: 'arraybuffer', // uni.request方式下载 文档里说这里只有text、arraybuffer,因此不能使用blob
header: {
"x-token": useUserStore().token,
},
}, false)
.then((res) => {
// console.log(res,res.data, 111)
if(res.status !== 200) return Promise.reject(res.errMsg || res.message || '下载失败')
pc_axios_download(
res.data,
fileName ||
(res.headers || res.header)["content-disposition"].replace(
/.+filename=(.+)$/,
"$1"
) ||
url.split("/").pop()
);
})
.catch((err) => {
console.log(err);
return Promise.reject(err);
});
};
3.axios 方式下载,这样pc端也可以用
/**
* 根据下载路径下载文件
* @param {*} url 下载路径,绝对路径,必选
* @param {*} fileName 下载文件名称,必选
* @returns
*/
export const downloadFileFromUrl = (url, fileName) => {
// uni方式 下载
// return uni_DownloadFile(url,fileName,{
// header: {
// "x-token": useUserStore().token,
// },
// })
// pc、h5 方式下载
return service
.request({
url: url,
method: "get",
responseType: "blob", // axios 等pc端通用下载
// responseType: 'arraybuffer', // uni.request方式下载 文档里说这里只有text、arraybuffer,因此不能使用blob
headers: {
"x-token": useUserStore().token,
},
}, false)
.then((res) => {
// console.log(res,res.data, 111)
if(res.status !== 200) return Promise.reject(res.errMsg || res.message || '下载失败')
pc_axios_download(
res.data,
fileName ||
(res.headers || res.header)["content-disposition"].replace(
/.+filename=(.+)$/,
"$1"
) ||
url.split("/").pop()
);
})
.catch((err) => {
console.log(err);
return Promise.reject(err);
});
};
注意:我这边自行将uni.request封装了下,调用方式向axios靠齐的,本来我这边调用没这么复杂,后面两种其实差不多。这里还是分开写的,避免有些同学搞不清楚。比如uni.request 是header不是headers,responseType只有arraybuffer没有blob等。
总结:
这边的项目目前采用webview引入uniapp开发的页面的方式,这样简单点,相当于iframe直接引入,很多pc端代码可以直接复用【这是个低代码平台,能少改点最好不过】【才不是因为直接打成app各种报错。。。】。后面发现嵌入到uniapp webview里面是没法使用a标签download下载这种方式的,虽然会弹出下载框但是会下载失败,浏览器上是正常下载的。因此采用的h5 plus 方式下载。
注意:
1.如果在h5 plus 下遇到下载失败,看下是否 plusready 完成
image.pngapp 端可以直接使用h5+ api
image.png
2.若遇到这个报错: Refused to get unsafe header "content-disposition"
image.png后端设置下resopnse content-disposition 应该就行了,不管他也没啥,不影响下载。
若对你有帮助,请点个赞吧,谢谢支持!
本文地址:https://www.jianshu.com/p/d827dd9ed17e?v=1691636612446,转载请注明出处,谢谢。
网友评论