formData
FormData 对象用以将数据变成键值对,以便于使用 XMLHttpRequest 来发送数据。其主要有两个作用:1. 用于发送表单数据,2. 发送File或者Blob类型的文件。
FormData对象的创建
let formData = new FormData();
向FormData追加数据
向 FormData 追加数据有两种方法:
formData.append('name','zs')
formData.set('age',18)
两种方法的区别是:如果指定的键已经存在,
FormData.set()会用新值覆盖已有的值,而FormData.append()会把新值添加到已有值集合的后面。
获取FormData中相应的值
let formData = new FormData()
formData.append('name','zs')
formData.append('name','ls')
// 获取key为name的第一个值
console.log(formData.get("name"))
// 获取key为name的所有值,返回为数组类型
console.log(formData.getAll("name"))
判断FormData中是否存在对应的key值
let formData = new FormData();
formData.append('name', 'zs')
formData.append('name', 'ls')
//判断是否包含key为name的数据
console.log(formData.has("name")); //true
//判断是否包含key为age的数据
console.log(formData.has("age")); //false
删除FormData删除数据
let formData = new FormData();
formData.append('name', 'zs')
formData.append('name', 'ls')
formData.delete('name')
console.log(formdata.get("name"))
Blob
什么是Blob
Blob 表示二进制类型的大对象。在数据管理系统中,将二进制数据存储为一个单一个体的集合。
Blob 对象表示一个不可变,原始数据的类文件对象。值得一提的是 File 接口基于 Blob。
Blob对象的创建
let blob = new Blob()
console.log(blob)
Blob对象的参数
let str = '123'
let blob = new Blob([str],{type:'text/plain'})
console.log(blob)
Blob 在创建的时候,可以传递两个参数,第一个参数是
数组,里面的东西会放进Blob中;第二个参数是对象,里面可以传递type去指定Blob数组内容的类型。
看前面控制台中有一个 text() 方法,调用这个方法可以获得一个成功的Promise对象,然后,我们就可以用 then 方法去进行下一步的操作啦。
使用Blob实现文件的下载和预览
下载文件
<body>
<input type="file" id="input">
<script>
let input = document.getElementById('input')
input.onchange = (e) => {
let file = e.target.files[0];
const link = document.createElement('a');
const blob = new Blob([file]);
// 设置a标签的href(点击地址)
link.href = URL.createObjectURL(blob);
// 设置a标签属性
link.setAttribute('download', 'hello.xlsx');
// 点击a标签
document.body.appendChild(link);
link.click();
// 移除a标签
document.body.removeChild(link);
}
</script>
</body>
图片的预览
<body>
<input type="file" id="input">
<script>
let input = document.getElementById('input')
input.onchange = (e) => {
let file = e.target.files[0];
let blob = new Blob([file])
let img = new Image()
let fileRead = new FileReader()
document.body.appendChild(img)
fileRead.onload = function(){
img.src = fileRead.result
}
fileRead.readAsDataURL(file)
}
</script>
</body>
Blob 对象中还提供了许多其他的方法,更加详细的可以参考 MDN:developer.mozilla.org/zh-CN/docs/… 和 这篇文章:zhuanlan.zhihu.com/p/161000123
文件的上传
使用FormData实现单一文件上传
工程结构
<template>
<div class="oneFile">
<h4>单一文件上传[FORM-DATA]</h4>
<div class="box">
<input
type="file"
class="upload_inp"
accept=".png,.jpg,.jpeg"
@change="fileChange"
ref="inputer"
/>
<div class="button_box">
<button class="upload_button select" @click="btnSelect">
选择文件
</button>
<button class="upload_button upload" @click="btnUpload">上传到服务器</button>
</div>
<img :src="src" alt="" v-if="isShow" />
</div>
</div>
</template>
首先,我们要实现的就是,如何点击 选择文件 这个按钮,从而触发 input 进行选择文件。
btnSelect() {
this.$refs.inputer.dispatchEvent(new MouseEvent("click"));
},
然后,我们就可以根据 input 这个按钮的 change 去对文件进行操作,获取到我们选择的文件。
fileChange(e) {
// 获取用户选中的文件
let files = e.target.files[0];
if (!files) return;
// 限制文件上传的格式(这是除了上面的 accept 的另一种校验文件格式的方法)
// if(!/(PNG|JPG|JPEG)/i.test(file.type)){
// alert('上传的文件只能是 PNG|JPG|JPEG')
// }
// 限制文件上传的大小
if (files.size > 2 * 1024 * 1024) {
alert("上传文件不能超过2MB");
}
this.file = files
},
最后,点击上传按钮进行发送请求就可以了。
btnUpload(){
if(!this.file){
alert('请您选择需要上传的图片')
}
// 把文件传递给服务器:FormData
let formData = new FormData()
formData.append('file',this.file)
formData.append('filename',this.file.name)
fileAxios.post('/upload_single',formData).then(data => {
if(data.code === 0){
alert(`文件上传成功~~~,您可以基于 ${data.servicePath} 访问这个资源`)
return
}
return Promise.reject(data.codeText)
}).catch(reason => {
alert('文件上传失败,请您稍后再试 ' + reason)
})
}
使用BASE64实现单一文件上传
把文件转成 BASE64 的模式。这里我们借用 FileReader 中的 readAsDataURL 方法。
changeBASE64(files) {
return new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.readAsDataURL(files);
fileReader.onload = (ev) => {
resolve(ev.target.result);
};
});
},
关于更加详细的
FileReader,可以阅读这篇文章:(99条消息) 原生js使用FileReader将文件转成base64_杨树林er的博客-CSDN博客_filereader 转base64
然后上传到服务器
async fileChange(e) {
// 获取用户选中的文件
let files = e.target.files[0];
if (!files) return;
// 拿到 base64 的图片
let BASE64 = await this.changeBASE64(files);
try {
let data = await fileAxios.post(
"/upload_single_base64",
{
file: encodeURIComponent(BASE64),
filename: files.name,
},
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
if (+data.code === 0) {
alert(
`恭喜您,文件上传成功,您可以基于 ${data.servicePath} 地址去访问~~`
);
return;
}
throw data.codeText;
} catch (err) {
alert("很遗憾,文件上传失败,请您稍后再试~~" + err);
}
},
进度管控
我们在发送请求的时候,我们可以传入第三个参数,借助 onUploadProgress 这个属性,去进行进度的管控。
async fileChange(e) {
// 获取用户选中的文件
let files = e.target.files[0];
if (!files) return;
try {
let formData = new FormData();
formData.append("file", files);
formData.append("filename", files.name);
let data = await fileAxios.post("/upload_single", formData, {
// 文件上传中的回调函数 xhr.upload.onprogress
onUploadProgress:(ev) => {
console.log(ev)
let { loaded, total } = ev;
this.loading = `${(loaded / total) * 100}%`;
},
});
if (+data.code === 0) {
alert(
`恭喜您,文件上传成功,您可以基于 ${data.servicePath} 访问该文件~~`
);
return;
}
throw data.codeText;
} catch (err) {
alert("很遗憾,文件上传失败,请您稍后再试~~"+err);
} finally {
this.loading = null
}
},
传入的
ev这个对象中,有一个total属性,表示已经上传的;total这个属性,表示总共需要上传的。所以,我们可以通过loaded/total从而得到上传的进度。
多文件上传
多文件上传的实质其实和单文件上传并没有什么差别。首先,我们需要给 input 增加一个属性 multiple
<input type="file" class="upload_inp" accept=".png,.jpg,.jpeg" @change="fileChange" ref="inputer" multiple/>
这样,我们就可以进行多文件的上传啦。
btnUpload() {
if (this.files.length === 0) {
alert("请您先选择要上传的文件~~");
return;
}
// 循环发送请求
this.files = this.files.map((item) => {
let fm = new FormData();
fm.append("file", item.file);
fm.append("filename", item.filename);
return fileAxios.post("/upload_single", fm).then((data) => {
if (+data.code === 0) {
return;
}
return Promise.reject(data.codeText);
});
});
Promise.all(this.files)
.then(() => {
alert("恭喜你,所有文件都上传成功");
})
.catch((err) => {
console.log(this.files);
alert("很遗憾,文件上传失败,请稍后在试~~" + err);
console.log(err)
})
.finally(() => {
this.files = [];
});
},
这里其实还是使用的上面上传单文件的接口,所以,我使用了一个循环,去单独给每一个文件发送一次请求,最后在使用
Promise.all()这个函数,去检测是否全部发送成功。
当然,你也可以将所有的文件全部都放到 FormData 这个对象中,最后在将 FormData 传给后端。
当然,具体的选择,还是要看你和后端的交流。
拖拽上传
拖拽上传的实现,其实主要依靠的是监听几个事件,主要是 drop 这个事件。
<div class="button_box" ref="button_box" v-on="{click:btnSelect,dragenter:dragenterBox,dragleave:dragleaveBox,dragover:dragoverBox,drop:dropBox}">
<span>拖拽上传</span>
</div>
async uploadFile(file){
try{
let fm = new FormData
fm.append('file',file)
fm.append('filename',file.name)
let data = await fileAxios.post('/upload_single',fm)
if(data.code === 0){
alert('文件上传成功')
return
}
throw data.codeText
}catch(err){
alert('文件上传失败~~'+err)
}finally{
this.$refs.button_box.style.backgroundColor = '#fff'
this.isRun=false
}
},
dragenterBox(){
console.log('进入')
},
dragleaveBox(){
console.log('离开')
},
dragoverBox(e){
console.log('区域内移动')
e.preventDefault()
},
dropBox(e){
console.log('放到容器中')
e.preventDefault()
// 获取文件
let files = e.dataTransfer.files[0]
if(!files) return
// 上传文件
this.uploadFile(files)
},
切片上传
为了避免上传大文件时上传超时,就需要用到切片上传,工作原理是:我们将大文件切割为小文件,然后将切割的若干小文件上传到 服务器 端,服务器端接收到被切割的小文件,然后按照一定的顺序将小文件拼接合并成一个大文件。
结构:
<input type="file" id="videoUpload" value="选择视频">
<button id="uploadBtn">上传视频</button>
<br>
<span id="uploadInfo"></span>
<br>
<progress value="0" id="uploadProgress"></progress>
功能实现:
let oUpload = document.getElementById("videoUpload");
let oBtn = document.getElementById("uploadBtn");
let oInfo = document.getElementById("uploadInfo");
let oProgress = document.getElementById("uploadProgress");
// 设置每一个分段的大小
let chunk_size = 64 * 1024;
// 上传的数量大小
let uploadedSize = 0;
// 上传后返回的结果
let uploadResult;
oBtn.addEventListener("click", async () => {
const file = oUpload.files[0];
if (!file) {
alert("请先选择文件~");
return;
}
const { name, type, size } = file;
const fileName = new Date().getTime() + "_" + name;
if (type !== "video/mp4" && type !== "video/ogg") {
alert("不支持该类型文件~~");
return;
}
oProgress.max = size;
// 避免发送太多请求
if (size > chunk_size * 100) {
chunk_size = size / 100;
}
while (uploadedSize < size) {
const fileChunk = file.slice(uploadedSize, uploadedSize + chunk_size);
const formData = createFormData({
name,
type,
size,
uploadedSize,
fileName,
file: fileChunk,
});
try {
uploadResult = await axios.post(
"http://127.0.0.1:3000/upload_video",
formData
);
console.log(uploadResult);
} catch (err) {
alert("上传失败" + err.message);
return;
}
uploadedSize += chunk_size;
oProgress.value = uploadedSize;
}
alert("上传成功");
oUpload.value = null;
createVideo(uploadResult.data.video_url);
});
function createFormData({ name, type, size, uploadedSize, fileName, file }) {
const fd = new FormData();
fd.append("name", name);
fd.append("type", type);
fd.append("size", size);
fd.append("uploadedSize", uploadedSize);
fd.append("fileName", fileName);
fd.append("file", file);
return fd;
}
function createVideo(src) {
const oVideo = document.createElement("video");
oVideo.controls = true;
oVideo.width = "500";
oVideo.src = src;
document.body.appendChild(oVideo);
}
参考文献及资料
从FormData到图片上传 - 掘金 (juejin.cn)
zhuanlan.zhihu.com/p/161000123
(99条消息) 原生js使用FileReader将文件转成base64_杨树林er的博客-CSDN博客_filereader 转base64
结语
好啦,本次分享就到这里。
文章如果有不正确的地方,欢迎指正,共同学习,共同进步。
若有侵权,请联系作者删除。