做设备推广的网站,如何建设一个好的企业网站,做网站接私活价格怎么算,去除wordpress版权前言
在项目开发的过程中#xff0c;经常会遇到上传和下载#xff0c;对于上传来说#xff0c;如果是小文件的话#xff0c;接口响应会比较快#xff0c;但是对于大文件#xff0c;则需要对其分片以减少请求体的大小和上传时间。
小文件上传
以Vue框架使用el-uplo…前言
在项目开发的过程中经常会遇到上传和下载对于上传来说如果是小文件的话接口响应会比较快但是对于大文件则需要对其分片以减少请求体的大小和上传时间。
小文件上传
以Vue框架使用el-upload为例直接上代码
templatedivel-uploadclassupload-demoactionyour_upload_api_url:on-successhandleSuccess:before-uploadbeforeUploadel-button sizesmall typeprimary点击上传/el-buttondiv slottip classel-upload__tip只能上传jpg/png文件且不超过500kb/div/el-upload/div
/templatescript
export default {methods: {handleSuccess(response, file) {// 处理上传成功的逻辑console.log(response, file);},beforeUpload(file) {// 在上传之前的操作例如限制文件类型、大小等const isJPG file.type image/jpeg || file.type image/png;if (!isJPG) {this.$message.error(只能上传jpg/png文件);}const isLt500K file.size / 1024 500;if (!isLt500K) {this.$message.error(文件大小不能超过500KB);}return isJPG isLt500K;},},
};
/scriptstyle scoped
/* 样式可以根据自己的需求进行调整 */
.upload-demo {display: flex;justify-content: center;align-items: center;height: 180px;
}
/style
在上述代码中
el-upload 组件用于处理文件上传通过 action 属性指定文件上传的接口。 :on-success 属性绑定一个方法在文件上传成功后触发。 :before-upload 属性绑定一个方法在文件上传之前触发可以在该方法中进行一些操作如限制文件类型和大小。 元素用于触发文件选择。 请注意替换 your_upload_api_url 为实际的文件上传接口。
分片上传
文件过大时就需要进行文件分片上传文件分片上传是一种将大文件拆分成小块分片并分别上传的策略这样可以更有效地处理大文件上传避免一次性上传整个文件可能遇到的网络问题和服务器限制。FormData 对象和一些前端框架/库如 axios通常与文件分片上传一起使用。
下面是一个简单的实现示例使用 FormData 和 axios 进行文件分片上传
templatedivinput typefile reffileInput changehandleFileChange /button clickstartUpload开始上传/button/div
/templatescript
import axios from axios;export default {data() {return {selectedFile: null,chunkSize: 1024 * 1024, // 每个分片的大小这里设置为1MB};},methods: {handleFileChange(event) {this.selectedFile event.target.files[0];},async startUpload() {if (!this.selectedFile) {alert(请选择文件);return;}// 计算总分片数量const totalChunks Math.ceil(this.selectedFile.size / this.chunkSize);// 循环上传分片for (let chunkIndex 0; chunkIndex totalChunks; chunkIndex) {const start chunkIndex * this.chunkSize;const end Math.min(start this.chunkSize, this.selectedFile.size);const chunk this.selectedFile.slice(start, end);const formData new FormData();formData.append(file, chunk);formData.append(chunkIndex, chunkIndex);formData.append(totalChunks, totalChunks);try {await axios.post(your_chunk_upload_api_url, formData);console.log(分片 ${chunkIndex 1} / ${totalChunks} 上传成功);} catch (error) {console.error(分片 ${chunkIndex 1} / ${totalChunks} 上传失败, error);// 处理上传失败的逻辑可以选择中止上传或重试return;}}console.log(文件上传完成);},},
};
/script上面是一个分片上传的示例在实际操作时遇到了一些问题
分片上传遇到的问题
问题1请求体过大如何处理
项目中遇到的文件最大约1个GB此时直接上传会报请求体过大的报错经过调试后发现文件最大传输为50MB。由于项目是依赖于平台属于平台的子项目因此在前后端联调时前端通过nginx转发到对应的接口上。在nginx配置里有50M大小的限制修改后生效。后台同事在排查后台代码及配置也发现了请求不能过大的限制条件即ingress中设置了请求的大小最终两者同时修改后生效
nginx中的配置修改如下
问题2前端如何获取上传进度条
在使用 axios 进行文件上传时你可以通过配置 onUploadProgress 属性来监听上传进度。onUploadProgress 允许你在上传过程中获取上传进度并执行相应的操作。
axios.post(your_upload_api_url, formData, {onUploadProgress: progressEvent {const percentCompleted Math.round((progressEvent.loaded * 100) / progressEvent.total);console.log(上传进度: ${percentCompleted}%);// 在这里可以更新进度条或执行其他操作},}).then(response {// 处理上传成功的逻辑console.log(response.data);}).catch(error {// 处理上传失败的逻辑console.error(上传失败, error);});需要注意的是这里的progressEvent.total并不一定和文件的大小相等处理百分比时尽量使用total而不是file.size。
问题3串行上传时文件上传没有问题但是并行上传时后台取到的文件片组装后错误
经过定位发现是在分片时最后一片会比较小如果串行上传时前端一片片按着顺序依次上传想优化上传速度使用并行上传时最后一片通常都会比前面的分片小导致最后一片先上传然后发生组装错误。经过测试可以采取以下思路1、前端最后一片在前面的分片都上传完毕后上传最后一片可使用for循环配合Promise.all处理2、后端在拿到所有的分片后再开始组装而不是边上传边组装。
问题4进度条上传达到100%后没有立刻返回结果
这是因为后台接收到文件后可能还有处理的时间但是文件已经传到了后台如果后台没有其他逻辑处理可以直接返回结果告知用户上传已完成 最后放上去部分代码
async handleUpload() {const file this.formData.file;// 初始化分片的大小可以自定义const chunkSize 200 * 1024 * 1024;// 计算分片的数量 传参时会用到const chunkCount Math.ceil(file.size / chunkSize);// 用于保存每个分片的信息const chunks [];if (file.size chunkSize) {// 分割文件为多个分片for(let i 0; i chunkCount; i) {const start i * chunkSize;const end Math.min(file.size, (i 1) * chunkSize);const chunk file.raw.slice(start, end);chunks.push(chunk);}} else {chunks.push(file.raw);}try {this.uploadLoading true;// 创建一个数组来存储每个分片上传的 Promiseconst uploadPromises [];for(let i 0; i chunks.length; i) {this.loadedSizeArr[i] 0;this.totalSizeArr[i] 0;}const fileFlag getRandomName();let percentage 1;if (chunks.length 1) {percentage (chunks.length-1) / chunks.length;}const headers {fileFlag,fileName: file.name,Content-Type: multipart/form-data,}// 遍历并上传每个分片for(let i 0; i Math.max(chunks.length - 1, 1); i) {const formData new FormData();formData.append(file, chunks[i]);formData.append(chunkNumber, String(i1));formData.append(totalChunks, String(chunkCount));// 创建分片上传的 Promiseconst uploadPromise util.post(your_upload_api_url, formData, {headers,onUploadProgress: (progressEvent) {if (progressEvent.lengthComputable) {this.loadedSizeArr[i] progressEvent.loaded;this.totalSizeArr[i] progressEvent.total;const loadedSizeTotal this.loadedSizeArr.reduce((accumulator, currentValue) accumulator currentValue, 0);const totalSizeTotal this.totalSizeArr.reduce((accumulator, currentValue) accumulator currentValue, 0);this.percent Math.round(loadedSizeTotal / totalSizeTotal * percentage * 100);}},})// 将 Promise 存储到数组中uploadPromises.push(uploadPromise);}// 使用 Promise.all 来等待所有分片上传完成Promise.all(uploadPromises).then(async () {if (chunkCount 1) {const formData new FormData();formData.append(file, chunks[chunks.length-1]);formData.append(chunkNumber, String(chunkCount));formData.append(totalChunks, String(chunkCount));await util.post(your_upload_api_url, formData, {headers,onUploadProgress: (progressEvent) {if (progressEvent.lengthComputable) {this.loadedSizeArr[chunks.length-1] progressEvent.loaded;this.totalSizeArr[chunks.length-1] progressEvent.total;const loadedSizeTotal this.loadedSizeArr.reduce((accumulator, currentValue) accumulator currentValue, 0);const totalSizeTotal this.totalSizeArr.reduce((accumulator, currentValue) accumulator currentValue, 0);this.percent Math.round((percentage loadedSizeTotal / totalSizeTotal / chunkCount) *100);}},})}// 所有分片上传完成后执行的逻辑this.percent 100;this.uploadLoading false;this.$notify({type: success,title: 成功,message: 上传成功,})})} catch (e) {this.$notify({type: error,title: 失败,message: 上传失败,});}},