JS-文件下载,实现在ios也是下载 而不是预览,

需求

通过A链接的方式,把从后台获取到的文件下载到本地,实现在移动端,PC端都能下载

问题

通过ajax请求后端生成的文件流之后,创建BLOB文件进行下载,在PC端和移动安卓端都可以实现下载到本地和对应的手机,而在IOS端的话,是直接预览对应的blob文件地址 而不是下载对应的文件流,解决在IOS上也能实现下载(仅限Safari浏览器)

步骤

1.通过request,responseType的值为 'arraybuffer',请求对应的文件流

uni.request({
				...handleRes,
				responseType: 'arraybuffer',
				success(res) {
					// console.log(res, '返回数据');
                    // 这是返回的文件流
					const file = res.data;
					
				},
				fail() {
					uni.hideLoading();
					
				}
			})

2.通过将文件流转成blob对象

					let blob = null;
					if (headerDis.indexOf(tyepList.toString()) != -1) {
						blob = new Blob([file], {
							type: 'application/octet-stream;charset=UTF-8',
						})
					} else {
						blob = new Blob([file], {
							type: headerInfo['content-type'] ||
								'application/octet-stream;charset=UTF-8',
						})
					}

3.通过创建A链接,通过download属性进行下载

async linkTodownloadFile(blob, fName) {
		if (window.navigator.msSaveOrOpenBlob) {
			window.navigator.msSaveBlob(blob, fName);
			return false;
		}

		const url = window.URL.createObjectURL(blob)
		const link = document.createElement('a')
		link.style.display = 'none'
		link.href = url;
		link.setAttribute('download', fName)
		document.body.appendChild(link)
		if (document.all) {
			link.click();
		} else {
			// 兼容 Firfox
			const evt = document.createEvent('MouseEvents');
			evt.initEvent('click', true, true);
			link.dispatchEvent(evt);
		}
		document.body.removeChild(link) // 下载完成移除元素
		window.URL.revokeObjectURL(url);
		uni.hideLoading();
		common.toast(107)
	}

注意:这里需要主要的在IOS端如果自定义blob的type的话 download属性就只加文件名 而不需要后缀,会形成双重后缀.而且如果类型为steam的话就会是预览 而设置成steam的话就会直接下载文件,也就是第二步做的判断原因

4.完整的代码

uni.request({
				url : 'xxx',
				responseType: 'arraybuffer',
				success(res) {
					// console.log(res, '返回数据');
					const file = res.data;
					const headerInfo = res.header;
					/** 获取文件名称 */
					let fName = '';
					/** 判断是否为可自定义类型还是默认 */
					const tyepList = ['pdf'];

					const headerDis = headerInfo['content-disposition'];

					if (headerDis) {
						const requestFileInfo = headerDis.split(';')[1];
						if (store.state.app.SYSTEM_INFO.platform == 'ios') {
							if (headerDis.indexOf(tyepList.toString()) != -1) {
								fName = requestFileInfo;
							} else {
								fName = requestFileInfo.split('.')[0];
							}

						} else {
							fName = requestFileInfo;
						}
					} else {
						/** 判断文件是否有类型,没有就拿效应头 */
						const fType = file.type || headerInfo['content-type'].split(';')[0];
						for (const key in fileTypeConfig) {
							if (fileTypeConfig[key].indexOf(fType) != -1) {
								if (store.state.app.SYSTEM_INFO.platform == 'ios') {
									fName = `${fileName}`;
								} else {
									fName = `${fileName}.${key}`;
								}
								break
							}
						}
					}

					/** 开始下载 */
					let blob = null;

					if (headerDis.indexOf(tyepList.toString()) != -1) {
						blob = new Blob([file], {
							type: 'application/octet-stream;charset=UTF-8',
						})
					} else {
						blob = new Blob([file], {
							type: headerInfo['content-type'] ||
								'application/octet-stream;charset=UTF-8',
						})
					}

					/** 判断是否为JSON 做逻辑处理 */
					if (fName.indexOf('json') != -1) {
						//通过FileReader读取数据
						const reader = new FileReader();
						// reader.readAsBinaryString(blob);
						reader.readAsText(blob, 'utf8');
						reader.onload = function() {
							var content = JSON.parse(this.result); //这个就是解析出来的数据
							if (content.code == 200) {
								// common.toast(fName);
								that.linkTodownloadFile(blob, fName);
								resolve();
							} else {
								isLoading && uni.hideLoading();
								common.toast(content.msg || 400);
							}
						}
					} else {
						// common.toast(fName);
						that.linkTodownloadFile(blob, fName);
						resolve();
					}
				},
				fail() {
					uni.hideLoading();
					common.toast(108)
					reject();
				}
			})

5.附件 fileTypeConfig的内容

export default {
	xls: 'application/vnd.ms-excel',
	xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
	csv: 'text/csv',
	doc: 'application/msword',
	docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
	pdf: 'application/pdf',
	ppt: 'application/vnd.ms-powerpoint',
	pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
	png: 'image/png',
	gif: 'image/gif',
	jpeg: 'image/jpeg',
	jpg: 'image/jpeg',
	mp3: 'audio/mpeg',
	aac: 'audio/aac',
	html: 'text/html',
	css: 'text/css',
	js: 'text/javascript',
	json: 'application/json',
	abw: 'application/x-abiword',
	arc: 'application/x-freearc',
	avi: 'video/x-msvideo',
	azw: 'application/vnd.amazon.ebook',
	bin: 'application/octet-stream',
	bmp: 'image/bmp',
	bz: 'application/x-bzip',
	bz2: 'application/x-bzip2',
	csh: 'application/x-csh',
	eot: 'application/vnd.ms-fontobject',
	epub: 'application/epub+zip',
	htm: 'text/html',
	ico: 'image/vnd.microsoft.icon',
	ics: 'text/calendar',
	jar: 'application/java-archive',
	jsonld: 'application/ld+json',
	mid: 'audio/midi audio/x-midi',
	midi: 'audio/midi audio/x-midi',
	mjs: 'text/javascript',
	mpeg: 'video/mpeg',
	mpkg: 'application/vnd.apple.installer+xml',
	odp: 'application/vnd.oasis.opendocument.presentation',
	ods: 'application/vnd.oasis.opendocument.spreadsheet',
	odt: 'application/vnd.oasis.opendocument.text',
	oga: 'audio/ogg',
	ogv: 'video/ogg',
	ogx: 'application/ogg',
	otf: 'font/otf',
	rar: 'application/x-rar-compressed',
	rtf: 'application/rtf',
	sh: 'application/x-sh',
	svg: 'image/svg+xml',
	swf: 'application/x-shockwave-flash',
	tar: 'application/x-tar',
	tif: 'image/tiff',
	tiff: 'image/tiff',
	ttf: 'font/ttf',
	txt: 'text/plain',
	vsd: 'application/vnd.visio',
	wav: 'audio/wav',
	weba: 'audio/webm',
	webm: 'video/webm',
	webp: 'image/webp',
	woff: 'font/woff',
	woff2: 'font/woff2',
	xhtml: 'application/xhtml+xml',
	xml: 'text/xml',
	xul: 'application/vnd.mozilla.xul+xml',
	zip: 'application/zip,application/x-zip-compressed',

}
相关推荐
Jiaberrr9 分钟前
JS实现树形结构数据中特定节点及其子节点显示属性设置的技巧(可用于树形节点过滤筛选)
前端·javascript·tree·树形·过滤筛选
我码玄黄1 小时前
THREE.js:网页上的3D世界构建者
开发语言·javascript·3d
爱喝水的小鼠1 小时前
Vue3(一) Vite创建Vue3工程,选项式API与组合式API;setup的使用;Vue中的响应式ref,reactive
前端·javascript·vue.js
小晗同学1 小时前
Vue 实现高级穿梭框 Transfer 封装
javascript·vue.js·elementui
WeiShuai1 小时前
vue-cli3使用DllPlugin优化webpack打包性能
前端·javascript
forwardMyLife1 小时前
element-plus的面包屑组件el-breadcrumb
javascript·vue.js·ecmascript
mez_Blog3 小时前
个人小结(2.0)
前端·javascript·vue.js·学习·typescript
珊珊而川3 小时前
【浏览器面试真题】sessionStorage和localStorage
前端·javascript·面试
森叶3 小时前
Electron 安装包 asar 解压定位问题实战
前端·javascript·electron
深情废杨杨3 小时前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js