上传
- form input 上传
设置form的action为后端页面,enctype=“multipart/form-data”,type=“post”
<form action='uploadFile.php' enctype="multipart/form-data" type='post'>
<input type='file'>
<input type='hidden' name='userid'>
<input type='hidden' name='signature'>
<button>提交</button>
</form>
form表单提交数据后会自动跳转到action指定的页面,为了禁止页面跳转,可以在页面中新建一个空的ifame,比如name=‘upload’,然后设置form的target=“Uploader”,form有一个target的属性,规定在何处打开action,这样form提交数据后就会仍停留在当前页。代码如下:
form action='uploadFile.php' enctype="multipart/form-data" type='post' target="uploader1">
<input type='file'>
<button>提交</button>
</form>
<ifrmae name='upload' id='uploader1'></iframe>
这样写的另一个好处是,可以知道什么时候上传完成并接收到后端的回调结果。比如上面这个例子,文件数据发送到了 ‘uploadFile.php’,假设该页面处理完数据后返回了一个地址,该地址会被写入到之前的iframe中。所以在ifame的onload函数触发时,也就是上传完成后,可以在iframe中读取到后端返回的数据。
var iframe = document.getElementById('upload1'); iframe.onload = function () {
var doc = window.frames['uploader1'].document; var pre =
doc.getElementsByTagName('pre'); var obj = JSON.parse(pre[0].innerHTML); }
使用这种方法时需要注意,iframe有跨域限制,创建出来的iframe的地址如果和当前页面地址不同源,会报错。这种情况下,建议大家在iframe的onload函数中,再次向后端请求一个接口获取文件地址,而不是直接去iframe里读取。或者返回这样的数据
<script type="text/javascript">
window.top.window[callback](data)
</script>
callback是和前端约定好的名字,上传完成后触发该函数并返回后端数据。
- js formData
https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/append
const data = new FormData();
data.append('file', file, file.name); // 表单 key, 表单 value, 传给服务器 fileName
axios.post({ // 上传接口
url,
data,
params: {
}
})
下载图片
前端下载图片分为两种情况同源图片下载
和非同源图片下载
同源图片下载方案
- 通过 a 标签自带 download 属性实现下载:
<a href="./img/logo.png" download="logo.png"></a>
- 通过 js 创建 a 标签并触发 click
const imgDownload = (url, fileName) => {
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.click()
};
imgDownload('./img/logo.png', 'logo.png');
非同源图片下载方案
- 将图片转化为blob或者base64
// 转为 blob
function urlToBlobAndDownload(url){
//实例化一个img对象
const img = new Image();
//设置img的图片路径
img.src = url;
//设置跨域解决
img.setAttribute('crossOrigin', 'Anonymous');
//img加载完后处理
img.onload = function() {
//创建一个canvas对象
const canvas = document.createElement('canvas')
//把图片的宽度设为canves的宽度
canvas.width = img.width
//把图片的高度设为canves的高度
canvas.height = img.height
//创建一个2d画布
const ctx = canvas.getContext('2d')
// 将img中的内容画到画布上
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
// 将画布内容转换为Blob
canvas.toBlob((blob) => {
// blob转为同源url
let blobUrl = window.URL.createObjectURL(blob)
// 创建a链接
const a = document.createElement('a')
a.href = blobUrl
a.download = ''
// 触发a链接点击事件,浏览器开始下载文件
a.click()
})
}
}
// 转为 base64
function urlToBase64AndDownload(url){
//实例化一个img对象
const img = new Image();
//设置img的图片路径
img.src = url;
//设置跨域解决
img.setAttribute('crossOrigin', 'Anonymous');
//img加载完后处理
img.onload = function() {
//创建一个canvas对象
const canvas = document.createElement('canvas')
//把图片的宽度设为canves的宽度
canvas.width = img.width
//把图片的高度设为canves的高度
canvas.height = img.height
//创建一个2d画布
const ctx = canvas.getContext('2d')
// 将img中的内容画到画布上
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
// 将画布内容转换为base64
let base64 = canvas.toDataURL()
// 创建a链接
const a = document.createElement('a')
a.href = base64
a.download = ''
// 触发a链接点击事件,浏览器开始下载文件
a.click()
}
}
坑:以上两种方案生成的图片格式大部分为 png 格式正常,但是 gif 的下载就为一帧,下面这种方案可以实现 gif 的下载,不改变格式
- 通过axios请求来实现图片下载
function downloadByAxios(url,name){
axios({
//设置图片路径
url:url,
//设置请求方法为get请求
method:'get',
//设置相应类型为blob
responseType: 'blob'
}).then(
//得到的是一个blob对象
res => {
let url = window.URL.createObjectURL(res.data)
const a = document.createElement('a');
a.href = url
a.download = name
a.click()
}
)
}
坑:如果是请求oss上的图片的话,会有一个跨域的提示,解决方案是去oss服务控制台设置跨域规则
下载接口返回的二进制流
在前端下载中,一般使用 a 标签下载,但有时候要通过后端接口,比如文件资源放在一个有网络限制的非公网环境,也就是说工网环境通过 url 不能请求到资源
这时候一般后端会帮你读这个文件,然后接口返回二进制流给前端下载
二进制流大概长这样
ÿØÿàJFIFÿÛC
&/"$&81;:7165=EXK=ATB56MiNT[^cdc<Jltl`sXac_ÿÛC--_?6?__________________________________________________ÿÀk "ÿÄÿÄH!1A"QRaq‘234STr’#¡±B¢Á$5Cbs‚Ñ%Dðcƒ“ñUádÿÄÿÄ'!1AQ"2Baq#ÿÚ?÷çKªu„;gáÄ«²’ãÔ¸„ÜÜó^ÖynQßÁ‡+ºËžç—õXÌã̬+t@tÜ@9BùørÏ/oVzÆoJÍÎçŒ×êG´ÙÙVÙV3€"[]oQ8àî/uÛö§våû—~”.îÑYɶ·S2fÃÜЬ®0=ý&[¾?ËÕ2òqøsœÃg\®gp$®“‹ïbè¯ÍTª{]”4XŽ+>OÆok†|®´„Hö›µä~’¨ÉfIéu®rÚ2[+Hãu›,röÞ~9c¸ˆÛöùúPDDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DEÊ.ÇýW
wžk‹<f)maÉ|ï×al•éý=ÕÒ5nŠá’’4²¨¬C3Næ‘rx¯‡S',¶i¼u1‡³½®¦©Œx0ë‹Þʳ_JÒ•×yf§”‡9®ºôóülÛý"ó'0ò*û]€KAì±sÁ„I~oŽªÃ*XnÖô’xs“äòco¥‡A—5ºNà:bã+³¥,õ!Å‚"N]n£šQ-ŽP5?“¦£^,l½¢[3Ö3Ä-/•ŠXŒ²ƒn‹OçñãrÊHïÔí×·D"u,¯¿:‘óDEA§ÿ8!²
ÒHe1ÆÐH$•›––&K(oSØgš^§°Ï59ÓI‘CzžÃ<Òõ=–y§?ðÒdQyì3Í?¼öæœÿÃIQCzžÃ<ÖoSØgšsÿ
%E
ê{óKÔöæœÿÃI‘EzžË<Òõ=†y§?ðÒTQyì³Í?¼öæœÿÃIQEýç°Ï5†Hó!ŽF€m}
Ó™¤Èˆ´âTróǂñ(¦X̦ªË¥‡‹ô_¢ÇáßýE}ù|mþîj‡ëéþ‰øúÿE}þOþöj‡¯ôOÃÿÖ¯¢Éã?{?µÃ¿×ú'áßëýô²Ëã?{?µ&áͤí¦FØÀkF‹¢.˜xpÃÔg,òËÝu`DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD=±ÿ(R•=±ÿ(\ò÷7©{£§‘íâÖ’˜Ô-\R0Ä^á–ÙOU×r¼E1:tì¼Ìm©¨’¢’(ŽÀ–ôƒüBÚ/CW‰1´sÔMΡö,-µáª¿YZcª‚ž'09Ç3órh\¦MWZØ šJYó8m!ØÇn<ôRÏEqÊa'M²FàGpàZž¾ªYè#ÏG¦â5ʺÕ1ÔÄ$ˆÜ0{×%“LÚUñHZØÜá ÂäCFÚŠ8^%’ƹïˆå.6惦±}.¹£
有两种做法
通过 Blob
如果后端返回 Blob 时,有两处坑。文件名
和 下载后打不开
文件名可以前端写死,但最好用后端返的 Content-disposition
这个在 network 内可以看到,但是前端不能直接获取。
需要后端设置 Access-Control-Expose-Headers: Content-disposition
使得浏览器将该字段暴露给前端
Eg:
请求参数添加 responseType: 'blob'
axios({
method: 'post',
responseType: 'blob', // 必须设置否则下载结束后打不开
data
}).then(({ data: ByteStream, headers }) => {
/* 准备工作
通过 content-disposition 获取文件名,需要后端设置
通过 content-type 获取 new Blob 参数, 指定文件 MIME 类型
*/
let { 'content-disposition': FileName, 'content-type': type } = headers;
FileName = decodeURIComponent(FileName.replace(/.*filename=/, '')); // decodeURIComponent 解码出文件名 replace看具体后端返什么
type = type.replace(';charset=utf-8', ''); // 同上
/* 创建 blob 进行下载 */
const blob = new Blob([ByteStream], { type });
if ('download' in document.createElement('a')) {
const elink = document.createElement('a');
elink.download = FileName;
elink.href = URL.createObjectURL(blob);
elink.style.display = 'none';
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
} else {
navigator.msSaveBlob(blob, FileName); // 兼容 IE10 +
}
});
- 通过 window.open
这个办法很简单,但前提是后端接口是 get 请求,post 没测试过,但 post 好像不能直接通过 open 打开
如果不是 get 请和后端商量(打一架)
const { fileId, fileName } = file;
open('/api_jur/file/downloadSingle?fileId=' + fileId); // vue-cli 环境, /api_jur 是后端 ip+port, 脚手架配了对应的反向代理
a download
只能用 http 协议,本地打开不管用
href 不能写完整的 http 网络地址,只能写相对服务器的地址
<a
href='https://www.baidu.com/img/flexible/logo/pc/result@2.png'
download='logo'
>
只能新标签打开 不能下载
</a>
<a href='logo.png' download='logo'>
正常下载
</a>