大文件上传
<!-- 上传组件 --><template>
<div>
<input type="file" @change="handleFileChange" />
<button @click="uploadFile">上传</button>
<div v-if="progress > 0 && progress < 100">
<progress :value="progress" max="100"></progress>
<span>{{ progress }}%</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
file: null,
chunkSize: 1024 * ¼00, // 2MB
totalChunks: 0,
uploadedChunks: 0,
progress: 0,
uploadId: null,
};
},
methods: {
handleFileChange(event) {
this.file = event.target.files;
this.validateFile();
},
validateFile() {
const allowedTypes = ['image/jpeg', 'image/png', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
if (!allowedTypes.includes(this.file.type)) {
alert('文件类型不支持');
return;
}
if (this.file.size > 1024 * 1024 * 10) { // 10MB
alert('文件大小超过限制');
return;
}
this.totalChunks = Math.ceil(this.file.size / this.chunkSize);
},
uploadFile() {
if (!this.file) {
alert('请选择文件');
return;
}
// 获取上传 ID
this.getUploadId().then((uploadId) => {
this.uploadId = uploadId;
this.uploadChunks();
});
},
getUploadId() {
return fetch('/api/upload/init', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileName: this.file.name,
fileSize: this.file.size,
fileType: this.file.type,
}),
}).then((response) => response.json()).then((data) => data.uploadId);
},
uploadChunks() {
const chunks = this.createChunks();
const promises = [];
for (let i = 0; i < this.totalChunks; i++) {
const chunk = chunks;
const chunkIndex = i;
const promise = this.uploadChunk(chunk, chunkIndex).catch((error) => {
console.error(`上传分片 ${chunkIndex} 失败:`, error);
return this.uploadChunk(chunk, chunkIndex); // 重试
});
promises.push(promise);
}
Promise.all(promises).then(() => {
this.completeUpload();
});
},
createChunks() {
const chunks = [];
const fileReader = new FileReader();
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
let start = 0;
while (start < this.file.size) {
const end = Math.min(start + this.chunkSize, this.file.size);
const chunk = blobSlice.call(this.file, start, end);
chunks.push(chunk);
start = end;
}
return chunks;
},
uploadChunk(chunk, index) {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('uploadId', this.uploadId);
formData.append('chunkIndex', index);
formData.append('chunk', chunk);
fetch('/api/upload/chunk', {
method: 'POST',
body: formData,
}).then((response) => {
if (response.ok) {
this.updateProgress(index);
resolve();
} else {
reject(response.statusText);
}
}).catch(reject);
});
},
updateProgress(index) {
this.uploadedChunks++;
this.progress = (this.uploadedChunks / this.totalChunks) * 100;
},
completeUpload() {
fetch('/api/upload/complete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
uploadId: this.uploadId,
}),
}).then((response) => response.json()).then((data) => {
if (data.success) {
alert('文件上传成功');
} else {
alert('文件上传失败');
}
});
},
},
};
</script> 1. 文件选择与预处置惩罚
起首,我们需要创建一个文件选择界面,并对文件进行预处置惩罚,包括文件类型判断和文件巨细判断。
2. 文件切片与并行上传
[*]文件切片:使用 File.prototype.slice 方法将文件切分为多个分片。
[*]并行上传:使用 Promise.all 并行上传所有分片。
3. 秒传与断点续传
[*]秒传:在上传之前,先查抄服务器上是否有雷同的文件。如果有,则直接返回上传成功的相应。
[*]断点续传:在上传过程中记录已上传的分片信息,如果上传中断,可以从上次中断的地方继续上传。
4. 错误重试与控制并发
[*]错误重试:在上传失败时,主动重试上传。
[*]控制并发:通过限制同时上传的分片数量来控制并发,防止服务器过载。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]