# 关于文件对象的一些描述
在前端处理文件时会经常遇到File、Blob、ArrayBuffer、FileReader、FormData这些文件操作相关的名词,有时候很熟悉但是具体怎么用还是要去搜索一番,为了以后在处理文件时能有一个清晰的认知,我们来搞清楚这些有关于文件对象和方法的基础概念和使用。
# 各种文件类型和方法
# Blob对象
Blob(Binary Large Object)对象表示一个不可变、原始数据的类文件对象,用来存储二进制数据。它可以按文本、二进制或者文件流的格式进行读取。可以用于a标签下载文件和导出文件。
创建Blob
我们可以通过Blob构造函数Blob(array[, options])创建一个Blob对象,也可以通过接口请求把文件读取成Blob对象进行返回。
// type是以什么的格式识别
const blob = new Blob(['hello'], { type: 'text/html'})
const blob1 = new Blob(['hello'], { type: 'application/pdf'})
const blob2 = new Blob(['a', 'b'])
const blob3 = new Blob(['<div style='color:red;'>This is a blob</div>']);
const blob4 = new Blob([{ "name": "abc" }])
实例属性和方法
Blob.size 包含数据的大小(字节)。
Blob.type 一个字符串,返回Blob数据的MIME 类型 (opens new window)。
Blob.arrayBuffer() 返回一个 promise,返回二进制ArrayBuffer数据。
Blob.bytes() 返回一个 promise,返回Uint8Array格式数据。
Blob.slice() 截取指定范围内的Blob数据。
Blob.stream() 返回一个ReadableStream可读文件流。
Blob.text() 返回一个 promise,返回UTF-8 格式的字符串。
# File文件对象
File文件对象继承自Blob,表示文件的相关信息,通过 <input type="file">
元素选择文件或者通过拖放操作得到,常用于从本地选择文件后进行文件上传、拖拽上传、文件预览、大文件分片等操作。
const dom = document.getElementById('input');
dom.addEventListener('change', function(event){
const file = event.files[0];
console.log(file);
});
# ArrayBuffer对象
ArrayBuffer就是文件的二进制数据缓冲区对象,是以数组进行处理的二进制数据,也称二进制数组。ArrayBuffer对象代表储存二进制数据的一段内存,所以无法直接操作,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。常用于图形渲染音视频处理等。
const buffer = new ArrayBuffer(32);
ArrayBuffer无法通过JSON序列化和保存到缓存中,也无法用于子组件传值,要想传值需要转成base64字符串传入,组件接收再转成ArrayBuffer。
# FileReader对象
FileReader可以把File或Blob文件对象读取成其他格式类型,如文本、数据URL(Base64)或者ArrayBuffer等,它主要就是用来转换文件格式。FileReader是一个构造函数,实例化后通过实例方法设置对应的操作,然后通过监听事件返回对应的结果。
事件监听:
abort 读取操作被中断时触发。 reader.onabort = function(event) {}
error 在读取操作发生错误时触发。 reader.onerror = function(event) {}
load 读取操作完成时触发。 reader.addEventListener('load', function(event) {})
loadstart 读取操作开始时触发。 reader.onloadstart = function(event) {}
loadend 读取操作结束时触发。 reader.onloadend = function(event) {}
progress 在读取Blob时触发。 reader.onprogress = function(event) {}
实例方法:
reader.abort() 中止读取操作。
reader.readAsArrayBuffer(file) 将文件内容读取成ArrayBuffe二进制数据。
reader.readAsDataURL(file) 将文件内容读取成base64编码字符串。
reader.readAsText(file, encoding) 将文件内容读取成指定特殊编码字符串,默认为utf-8。
通过FileReader构造函数生成实例进行读取文件对象:
var file = e.files[0];
// FileReader使用
var reader = new FileReader();
// 以URL格式读取文件,base64格式
reader.readAsDataURL(file)
// 读取成ArrayBuffer
reader.readAsArrayBuffer(file)
// 读取成特殊编码
reader.readAsText(file, {type: 'utf-8'})
// 异步读取
reader.onload = function(e) {
const result = e.target.result;
console.log(result);
}
# FormData
FormData 是JS内置对象,常用于表单提交上传文件或发送二进制数据。
const file = event.files[0];
// 创建一个空的 FormData 对象
const formData = new FormData();
// 设置上传表单字段
formData.append('file ', file);
formData.append('type', 'upload');
// 其他方法
formData.get('type');
formData.delete('type');
formData.has('type');
// 上传文件
uploadFileApi(formData).then(res => {
console.log(res);
});
FormData不能直接设置数组,需要for循环一个一个进行设置。并且无法直接通过打印进行查看。
var arr = [1, 2, 3];
const formData = new FormData();
// 如果是数组,需要循环添加,并且要加[]
arr.forEach(item => {
formData.append("arr", item);
})
// 或者
arr.forEach((item, index) => {
formData.append(`arr[${index}]`, item);
});
// 如果是对象需使用JSON.stringify进行序列化
const obj = {a: 1};
formData.append('obj', JSON.stringify(obj));
# URL.createObjectURL
URL.createObjectURL() 是一个非常有用的API,用于创建一个指向 Blob、File 或 MediaSource 对象的临时 URL,这个 URL 可以被用作a/img/video/audio等元素的 src 属性。常用于文件下载、文件临时预览等功能。
// 基础用法
const objectURL = URL.createObjectURL(Blob|File|MediaSource);
const blob = new Blob(['Hello, world!'], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
console.log(url); // 输出类似于 "blob:http://example.com/12345678-1234-1234-1234-1234567890ab"
// 图片预览
const fileInput = document.getElementById('fileInput');
const preview = document.getElementById('preview');
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const url = URL.createObjectURL(file);
preview.src = url;
// 释放临时 URL(当不再需要时)
preview.onload = () => {
URL.revokeObjectURL(url);
};
}
});
# 各种文件类型相互转换
# File、Blob转其他类型
使用FileReader的方法将File或Blob转成Base64和ArrayBuffer。
// 使用readAsDataURL方法转base64
function fileOrBlobToBase64(blob, callback) {
const reader = new FileReader();
reader.onload = function(e) {
callback(e.target.result); // 这是一个包含 base64 数据 URL 的字符串
};
reader.readAsDataURL(blob);
}
// 使用readAsArrayBuffer方法转ArrayBuffer
function blobToArrayBuffer(blob, callback) {
const reader = new FileReader();
reader.onload = function(e) {
callback(e.target.result);
};
reader.readAsArrayBuffer(blob);
}
使用Blob对象的arrayBuffer()方法(推荐)
// Blob转arrayBuffe
async function blobToArrayBuffer(blob) {
const arrayBuffer = await blob.arrayBuffer();
return arrayBuffer;
}
# ArrayBuffer转Blob
要将 ArrayBuffer 转换为 Blob,你可以直接使用 Blob 构造函数,指定你想要的 MIME 类型。
/**
* @desc ArrayBuffer 转换为 Blob
* @param {ArrayBuffer} buffer 二进制文件流
* @param {String} type MIME 类型
* */
function arrayBufferToBlob(buffer, type) {
return new Blob([buffer], {type: type});
}
# ArrayBuffer转File
把ArrayBuffer转成File对象,首先将其转换为 Blob,然后使用 new File() 构造函数。需要注意的是,需要提供文件名和 MIME 类型等信息。
/**
* @desc ArrayBuffer 转换为 File
* @param {ArrayBuffer} arrayBuffer 二进制文件流
* @param {String} fileName 文件名
* @param {String} mimeType MIME 类型
* */
function arrayBufferToFile(arrayBuffer, fileName, mimeType) {
const blob = new Blob([arrayBuffer], {type: mimeType});
return new File([blob], fileName, {type: mimeType});
}
# ArrayBuffer和Base64互转
通过手动遍历Uint8Array并结合btoa和atob实现,btoa将字符串编码为Base64 格式。atob将Base64 编码的字符串解码回原始字符串。
// ArrayBuffer转Base64
function arrayBufferToBase64(arrayBuffer) {
// 将 ArrayBuffer 转换为 Uint8Array(字节数组)
const uint8Array = new Uint8Array(arrayBuffer);
// 将字节数组转换为二进制字符串
let binaryString = '';
for (let i = 0; i < uint8Array.length; i++) {
binaryString += String.fromCharCode(uint8Array[i]);
}
// 使用 btoa 将二进制字符串转换为 Base64
return btoa(binaryString);
}
// Base64 转 ArrayBuffer
function base64ToArrayBuffer(base64String) {
// 使用 atob 将 Base64 字符串解码为二进制字符串
const binaryString = atob(base64String);
// 创建一个 Uint8Array 来存储字节数据
const uint8Array = new Uint8Array(binaryString.length);
// 将二进制字符串的每个字符转换为字节
for (let i = 0; i < binaryString.length; i++) {
uint8Array[i] = binaryString.charCodeAt(i);
}
// 返回 ArrayBuffer
return uint8Array.buffer;
}
通过使用 FileReader 实现互转
// ArrayBuffer转Base64
function arrayBufferToBase64UsingFileReader(arrayBuffer) {
return new Promise((resolve, reject) => {
const blob = new Blob([arrayBuffer]);
const reader = new FileReader();
reader.onload = () => {
const dataURL = reader.result; // Data URL 格式
resolve(dataURL.split(',')[1]); // 去掉前缀
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
// Base64 转 ArrayBuffer
function base64ToArrayBufferUsingFileReader(base64String) {
return new Promise((resolve, reject) => {
const byteString = atob(base64String); // 解码 Base64
const uint8Array = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
uint8Array[i] = byteString.charCodeAt(i);
}
const blob = new Blob([uint8Array]);
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
}
# 获取文件内容
获取文件内容可以直接从本地文件系统上传,也可以通过读取本地地址和网络地址进行获取。从本地文件系统上传可以直接通过input获取;本地地址和网络地址则需要通过Ajax或fetch请求把内容下载下来才可以。
1、Ajax通过responseType参数来设置响应数据类型。
text 默认 返回text文本类型。
document Document对象,返回 XML 格式数据时使用。
json 返回JSON数据格式。
blob 返回二进制数据的Blob对象。
arrayBuffer 返回二进制数据的ArrayBuffer对象。
// 获取文件内容
function downloadUrlFile(url) {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = () => {
if (xhr.status === 200) {
// 获取文件blob数据
console.log(xhr.response);
}
}
xhr.send()
}
2、fetch设置响应数据类型。
response.text():返回文本字符串。
response.json():返回JSON 对象。
response.blob():返回二进制 Blob 对象。
response.formData():返回FormData 表单对象。
response.arrayBuffer():返回二进制 ArrayBuffer 对象。
// 获取文件内容
function downloadUrlFile(url) {
fetch(url)
.then((res) => res.blob())
.then(data => {
console.log(data);
})
}
# 文件下载
文件下载一般是通过接口请求把文件读取成blob格式数据返回,然后通过URL.createObjectURL()将Blob对象转成临时URL,并手动创建a标签模拟点击进行保存到本地。
// 文件下载
function downloadFile(url, params) {
axios({
url,
method: 'get',
params,
responseType: 'blob'
}).then(res => {
let blob = new Blob([res]);
let url = URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = "文件名";
a.click();
// 释放临时 URL
URL.revokeObjectURL(url);
})
}
/**
* 根据文件url获取文件名
* @param url 文件url
*/
getFileName(url) {
const num = url.lastIndexOf('/') + 1
let fileName = url.substring(num)
// 把参数和文件名分割开
fileName = decodeURI(fileName.split('?')[0])
return fileName
}
# 文件上传
文件上传需要通过<input type="file">
获取文件内容后,通过FormData格式进行上传,我们借助Element 的上传组件来实现文件上传功能。
<template>
<el-dialog title="上传文件" :visible="true" width="500px" @close="closeDialog">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form-item label="选择文件" prop="filePath">
<div class="upload">
<div class="upload-wrap" v-if="!fileList.length">
<!-- 上传组件 -->
<el-upload
action="#"
:auto-upload="false"
:show-file-list="false"
:on-change="handleUploadChange"
>
<!-- 自定义上传样式 -->
<slot>
<el-button type="primary" plain icon="el-icon-upload2" size="small" :loading="uploading">点击上传文件</el-button>
</slot>
</el-upload>
</div>
<div class="uplod-file" v-if="fileList.length">
<div class="file-item" v-for="(item, index) in fileList" :key="index">
<div class="item-name text-line-1">{{ item.name }}</div>
<div class="item-del">
<i class="el-icon-delete" @click="delUploadFile(index)"></i>
</div>
</div>
</div>
</div>
</el-form-item>
</el-form>
<template slot="footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="submitForm">确 认</el-button>
</template>
</el-dialog>
</template>
<script>
/**
* @desc 文件上传
* @author cxx
* */
import { uploadFileApi } from '@/api/public'
export default {
name: 'UploadFile',
data() {
return {
uploading: false,
saveLoad: false,
// 基础信息表单
formData: {
filePath: '',
percent: 0 // 上传进度
},
// 基础信息商品校验
formRules: {
filePath: [{ required: true, message: '请选择', trigger: ['change'] }]
},
fileList: []
}
},
methods: {
// 上传前验证
handleBeforeUpload(file) {
const { name, size } = file
const fileExtension = name.split('.').pop()
// 类型验证
const limitTypeList = ['pdf']
const limitType = limitTypeList.includes(fileExtension)
if (!limitType) {
this.$modal.msgError(`不支持${name}文件类型上传!`)
// return false
}
// 文件大小验证
const limitSize = size / 1024 / 1024 < 10
if (!limitSize) {
this.$modal.msgError(`${name}文件不可大于 10MB!`)
// return false
}
return limitType && limitSize
},
// 上传文件内容
handleUploadChange(elFile) {
const that = this
const file = elFile.raw
console.log('文件对象', file)
// 上传前格式大小验证
if (!this.handleBeforeUpload(file)) return
// 通过FormData格式上传
const formData = new FormData()
formData.append('file', file)
formData.append('bizType', 'content')
this.uploading = true
uploadFileApi(formData, {
//获取文件上传的进度
onUploadProgress(e) {
let completeProgress = ((e.loaded / e.total * 100) | 0)
that.formData.percent = completeProgress
}
}).then(data => {
console.log(data)
this.fileList.push(file)
this.formData.filePath = data.data
this.$refs.formRef.clearValidate()
}).finally(() => {
this.uploading = false
})
},
// 删除文件
delUploadFile(index) {
this.$confirm(`确定删除该文件?`, '提示', {
customClass: 'el-confirm-custom',
cancelButtonText: '取消',
confirmButtonText: '删除',
showClose: false,
center: true
}).then(() => {
this.fileList.splice(index, 1)
this.formData.filePath = ''
})
},
// 点击完成按钮
submitForm() {
this.$refs.formRef.validate(valid => {
// 验证不通过
if (!valid) return
const params = {
fileUrl: this.formData.filePath,
id: this.studentId
}
this.saveLoad = true
testApi(params)
.then(res => {
this.saveLoad = false
this.$message.success('操作成功!')
this.$emit('closeDialog', true)
})
.catch(err => {
this.saveLoad = false
})
})
},
// 返回列表页面
closeDialog() {
this.$emit('closeDialog')
}
}
}
</script>
<style lang="scss" scoped>
// 上传组件
.upload {
width: 100%;
.upload-wrap {
margin-bottom: 10px;
}
.uplod-file {
width: 100%;
.file-item {
display: flex;
align-items: center;
width: 100%;
overflow: hidden;
.item-name {
max-width: 300px;
font-weight: 400;
font-style: 14px;
color: #6B5CDA;
line-height: 22px;
}
.item-del {
margin-left: 12px;
color: #888888;
cursor: pointer;
}
}
}
}
</style>
接口请求设置
// 上传文件接口,onUploadProgress用户获取上传进度
export function uploadFileApi(parameter, {
onUploadProgress
}) {
return request({
url: api.uploadFile,
method: 'POST',
data: parameter,
onUploadProgress
})
}