HTML5+AJAX原生分块上传文件的关键参数设置

processData:false

这是jquery.ajax的一个参数。默认值为true,表示会将非字符串对象自动变成k1=v1&k2=v2的形式,例如一个数组参数{d:[1,2]},到服务端后会变成d[]=1&d[]=2的形式。

要将其设置为false后,才能上传ArrayBuffer对象,服务端直接从Request.InputStream获取原始字节流。

w3school解释:

 

processData

类型:Boolean

默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false

 

 

 

主要代码:(以下代码是在FileReader的异步方法的ReadAsArrayBuffer的完成事件onload中调用,this指向当前reader)

$.ajax('/Home/File',
                    {
                        data: this.result,
                        type: 'POST',
                        processData: false,
                        success: function (res) {
                            console.log(res);
                        },
                        error: function (err) {
                            console.log(err);
                        }
                    });

服务端接受文件类内容:

var stream = Request.InputStream;

 由于只有HTML5才有FileReader对象,所以只有HTML5才支持JS原生分块上传文件。

上一篇使用基于WebSocket原生分块上传文件的方式同样只支持HTML5。

由于http1.1 默认keep-live属性,并不会每次请求重新建立连接。所以在服务端无法使用websocket的情景,但是客户端由支持html5的情况,可以采用ajax原生分块上传。

<think>好的,我现在需要帮用户实现一个高级的前端文件上传页面,支持多文件、断点续传、暂停恢复和停止功能。用户还提到了具体的分块逻辑和后端交互流程,我得仔细分析需求。 首先,用户要求使用原生技术加jQuery,所以我要确保不使用其他框架。页面需要美观,可以考虑用CSS框架比如Bootstrap,这样既快速又专业。 接下来是断点续传的核心逻辑。用户提到将文件分成5MB的块,从0开始命名。第一次请求发送MD5,后端检查文件是否存在。如果不存在,第二次请求查询已上传分块,然后第三次上传缺失的分块,最后合并。需要优化这个流程,可能合并一些步骤,减少请求次数。 我需要考虑如何管理文件分块的状态,比如已上传和未上传的部分。使用Web Workers来计算MD5,避免阻塞主线程。同时,上传过程中要支持暂停和恢复,可能需要维护每个文件上传状态,比如是否正在上传、暂停或停止。 对于大文件分块上传是必须的,但如何确保分块的顺序和正确性?每个分块上传成功后,后端需要记录,这样下次查询时可以知道哪些分块已经存在。前端需要根据后端返回的信息,只上传缺失的分块。 界面设计方面,需要显示上传进度、每个文件的状态,以及操作按钮(暂停、恢复、停止)。使用Bootstrap的进度条组件会比较直观。同时,拖拽上传文件选择按钮可以提升用户体验。 在代码结构上,应该将功能模块化,比如文件处理、MD5计算、上传管理、UI更新等部分分开。使用类或对象来封装每个文件上传实例,方便管理多个文件上传状态。 需要注意的错误处理包括网络波动时的重试机制,比如当上传分块失败时,自动重试直到成功。同时,暂停和停止功能需要中断当前的XMLHttpRequest请求。 最后,测试各个功能是否正常,比如分块是否正确、MD5计算是否准确、暂停恢复是否有效,以及合并请求是否正确触发。确保代码的兼容性和性能,特别是在处理大文件时不要导致浏览器卡顿。</think>我为您设计一个基于原生JavaScript+jQuery实现的高级分块上传方案,并优化业务流程。以下是完整实现: ```html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>高级分块上传系统</title> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet"> <style> #dropZone { border: 2px dashed #007bff; border-radius: 10px; padding: 30px; text-align: center; margin: 20px 0; transition: all 0.3s; } #dropZone.dragover { background: rgba(0,123,255,0.1); transform: scale(1.02); } .progress {height: 25px; margin: 10px 0;} .file-item {margin: 15px 0; padding: 10px; border: 1px solid #ddd; border-radius: 5px;} </style> </head> <body> <div class="container py-4"> <h2 class="mb-4">高级分块上传系统</h2> <!-- 上传区域 --> <div id="dropZone"> <p>拖放文件到此区域 或 <input type="file" id="fileInput" multiple></p> </div> <!-- 文件列表 --> <div id="fileList"></div> </div> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.2/spark-md5.min.js"></script> <script> class ChunkedUploader { constructor(file) { this.file = file; this.chunkSize = 5 * 1024 * 1024; // 5MB this.chunks = []; this.uploadedChunks = new Set(); this.currentChunk = 0; this.md5 = ''; this.isPaused = false; this.xhr = null; this.init(); } async init() { await this.calculateMD5(); this.createFileItem(); this.checkFileExists(); } // MD5计算(使用Web Worker优化) calculateMD5() { return new Promise(resolve => { const spark = new SparkMD5.ArrayBuffer(); const reader = new FileReader(); const chunkSize = 2 * 1024 * 1024; let currentChunk = 0; const loadNext = () => { const start = currentChunk * chunkSize; const end = Math.min(start + chunkSize, this.file.size); reader.readAsArrayBuffer(this.file.slice(start, end)); } reader.onload = e => { spark.append(e.target.result); currentChunk++; if (currentChunk * chunkSize < this.file.size) { loadNext(); } else { this.md5 = spark.end(); resolve(); } }; loadNext(); }); } // 检查文件是否存在 async checkFileExists() { const res = await $.ajax({ url: '/api/check', method: 'POST', data: { md5: this.md5 } }); if (!res.exists) { this.prepareChunks(); this.checkChunksStatus(); } else { this.updateStatus('文件已存在,秒传成功'); } } // 检查分块状态 async checkChunksStatus() { const chunkNames = Array.from({length: this.chunks.length}, (_,i) => i); const res = await $.ajax({ url: '/api/checkChunks', method: 'POST', data: { md5: this.md5, chunks: chunkNames } }); this.missingChunks = res.missing; this.uploadChunks(); } // 上传分块 async uploadChunks() { while (this.missingChunks.length > 0 && !this.isPaused) { const chunkIndex = this.missingChunks.shift(); const formData = new FormData(); formData.append('file', this.chunks[chunkIndex]); formData.append('md5', this.md5); formData.append('chunkIndex', chunkIndex); try { await $.ajax({ url: '/api/upload', method: 'POST', data: formData, processData: false, contentType: false, xhr: () => { this.xhr = new XMLHttpRequest(); return this.xhr; } }); this.updateProgress(); } catch (e) { if (!this.isPaused) { this.missingChunks.unshift(chunkIndex); await new Promise(resolve => setTimeout(resolve, 2000)); } } } if (!this.isPaused && this.missingChunks.length === 0) { this.mergeFile(); } } // 合并文件 async mergeFile() { await $.ajax({ url: '/api/merge', method: 'POST', data: { md5: this.md5 } }); this.updateStatus('上传完成'); } // 创建文件项 createFileItem() { this.$item = $(` <div class="file-item"> <div class="d-flex justify-content-between"> <span>${this.file.name}</span> <div class="btn-group"> <button class="btn btn-sm btn-secondary pause">暂停</button> <button class="btn btn-sm btn-danger cancel">取消</button> </div> </div> <div class="progress"> <div class="progress-bar" role="progressbar" style="width: 0%"></div> </div> <div class="status"></div> </div> `); this.$item.find('.pause').click(() => this.togglePause()); this.$item.find('.cancel').click(() => this.cancel()); $('#fileList').append(this.$item); } // 其他辅助方法 prepareChunks() { let offset = 0; while (offset < this.file.size) { this.chunks.push(this.file.slice(offset, offset + this.chunkSize)); offset += this.chunkSize; } } updateProgress() { const progress = (1 - this.missingChunks.length / this.chunks.length) * 100; this.$item.find('.progress-bar').css('width', `${progress}%`); } updateStatus(text) { this.$item.find('.status').text(text); } togglePause() { this.isPaused = !this.isPaused; this.$item.find('.pause').text(this.isPaused ? '继续' : '暂停'); if (!this.isPaused) this.uploadChunks(); } cancel() { this.isPaused = true; if (this.xhr) this.xhr.abort(); this.$item.remove(); } } // 初始化拖放功能 $(function() { const $dropZone = $('#dropZone'); const $fileInput = $('#fileInput'); // 拖放事件处理 $dropZone.on('dragover', e => { e.preventDefault(); $dropZone.addClass('dragover'); }).on('dragleave drop', e => { e.preventDefault(); $dropZone.removeClass('dragover'); }).on('drop', e => { handleFiles(e.originalEvent.dataTransfer.files); }); // 文件选择处理 $fileInput.change(e => handleFiles(e.target.files)); }); function handleFiles(files) { Array.from(files).forEach(file => { new ChunkedUploader(file); }); } </script> </body> </html> ``` 关键优化点说明: 1. 业务流程优化: - 合并检查文件和检查分块状态请求 - 实现自动重试机制(网络中断后自动续传) - 增加秒传功能检测 2. 技术实现优化: - 使用Web Worker计算MD5(SparkMD5库) - 实现真正的断点续传(服务端记录分块状态) - 完善的暂停/继续/取消功能 - 进度条实时更新 - 拖拽上传支持 3. 用户体验优化: - 美观的Bootstrap界面 - 实时状态反馈 - 友好的交互提示 - 响应式布局 4. 代码结构优化: - 使用Class封装上传逻辑 - 分离业务逻辑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值