form + iframe(项目中不多用到,本文不涉及)前端
form表单提交的方式是全部浏览器都支持的,借助iframe是为了实现不刷新界面上传git
FormData + XHR2 + FileReader + canvasgithub
FormData接口提供了一种轻松构造一组表示表单字段及其值的键/值对的方法,而后可使用XMLHttpRequest.send()方法轻松地发送这些值。若是将编码类型设置为“multipart/form-data”,则使用与表单相同的格式。ajax
经常使用方法:编程
FormData.append(0; // 添加键值对 FormData.delete(); // 删除键值对 FormData.entries(); // 返回容许遍历此对象中包含的全部键/值对的迭代器
...具体还有不少方法能够参考网站:FormDatacanvas
使用XMLHttpRequest (XHR)对象能够与服务器交互。您能够从URL获取数据,而无需让整个的页面刷新。这使得Web页面能够只更新页面的局部,而不影响用户的操做。XMLHttpRequest在 Ajax 编程中被大量使用。数组
经常使用属性和方法浏览器
const xhr = new XMLHttpRequest() // 经常使用属性 xhr.onreadystatechange // 当readyState属性发生变化时调用的函数 xhr. readyState // 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。 0: 请求未初始化 1: 服务器链接已创建 2: 请求已接收 3: 请求处理中 4: 请求已完成,且响应已就绪 xhr.responseText // 包含对请求的响应,若是请求未成功或还没有发送,则返回null xhr.timeout // 表示该请求的最大请求时间(毫秒),超过该时间请求会自动结束。 xhr.upload // 表示上传过程。 // 经常使用方法 xhr.abort() // 停止请求 xhr.open() // 初始化一个请求。该方法只能JavaScript代码中使用 xhr.setRequestHeader() // 设置HTTP请求头的值 xhr.send() // 发送请求。若是请求是异步的(默认),那么该方法将在请求发送后当即返回
更多关于XMLHttpRequest的信息点击:XMLHttpRequest服务器
对象容许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。app
其中File对象能够是来自用户在一个<input>元素上选择文件后返回的FileList对象,也能够来自拖放操做生成的 DataTransfer对象,还能够是来自在一个HTMLCanvasElement上执行mozGetAsFile()方法后返回结果。
属性:
FileReader.error // 表示在读取文件时发生的错误 FileReader.readyState // 表示FileReader状态的数字。0:尚未加载任何数据; 1:数据正在被加载;2:已完成所有的读取请求 FileReader.result // 文件的内容。该属性仅在读取操做完成后才有效,数据的格式取决于使用哪一个方法来启动读取操做。
事件处理
FileReader.onabort= ()=>{} // 该事件在读取操做被中断时触发 FileReader.onerror = ()=>{} // 该事件在读取操做发生错误时触发。 FileReader.onload = ()=>{} // 该事件在读取操做完成时触发。 FileReader.onloadstart = ()=>{} // 该事件在读取操做开始时触发 FileReader.onloadend = ()=>{} // 该事件在读取操做结束时(要么成功,要么失败)触发 FileReader.onprogress = ()=>{} // 该事件在读取Blob时触发
方法
FileReader.abort() // 停止读取操做。在返回时,readyState属性为DONE FileReader.readAsArrayBuffer() // 开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象 FileReader.readAsDataURL() // 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容 FileReader.readAsText() // 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。
<form id="uploadForm" method="POST" action="upload" enctype="multipart/form-data"> <input type="file" id="myFile" name="file" /> <input type="submit" value="提交" /> </form>
全部浏览器都支持的上传方式,且submit提交后页面会刷新。
action: 提交地址
enctype的常见类型(告诉服务器咱们发送过去的数据是用哪一种格式进行编码的)
【实现步骤】
HTML代码
<input type="file" name="file" accept=“image/*” onchange='handleInputChange(event)'>
一、监听input的change事件
function handleInputChange (event) { const file = event.target.files[0]; // 获取当前选中的文件 const imgMasSize = 1024 * 1024 * 10; // 限制大小10MB // 检查文件类型 if(['jpeg', 'png', 'gif', 'jpg'].indexOf(file.type.split("/")[1]) < 0){ // 不支持该文件类型 } // 文件大小限制 if(file.size > imgMasSize ) { // 文件大小自定义限制 } // 图片压缩处理函数 transformFileToDataUrl(file); }
二、将file转成dataUrl
function transformFileToDataUrl (file) { const imgCompassMaxSize = 200 * 1024; // 超过 200k 就压缩 // 存储文件相关信息 imgFile.type = file.type || 'image/jpeg'; imgFile.size = file.size; imgFile.name = file.name; imgFile.lastModifiedDate = file.lastModifiedDate; // 封装好的函数 const reader = new FileReader(); // file转dataUrl是个异步函数,onload表示读取完成了 reader.onload = function(e) { const result = e.target.result; if(result.length < imgCompassMaxSize) { compress(result, processData, false ); // 图片不压缩 } else { compress(result, processData); // 图片压缩 } }; reader.readAsDataURL(file); }
三、canvas进行压缩的处理
function compress (dataURL, callback, shouldCompress = true) { const img = new window.Image(); // new 一个图片对象 img.src = dataURL; // 经过fileReader读取到的base64数据 img.onload = function () { // 一、建立canvas上下文 const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 获取图片宽高赋值给canvas绘图 canvas.width = img.width; canvas.height = img.height; // 绘制出一张canvas图片 ctx.drawImage(img, 0, 0, canvas.width, canvas.height); let compressedDataUrl; if(shouldCompress){ compressedDataUrl = canvas.toDataURL(imgFile.type, 0.2); // 后面的系数是绘图输出图片质量 } else { compressedDataUrl = canvas.toDataURL(imgFile.type, 1); // 不改变原图质量 } document.getElementById('preview').appendChild(img); callback(compressedDataUrl); // 最后图片压缩好后,去进行base64 -> blob的转换(传递到后台) } }
四、把Blob append进FormData中;
function processData (dataURL) { const binaryString = window.atob(dataURL.split(',')[1]); // window.atob对用base-64编码过的字符串进行解码 const arrayBuffer = new ArrayBuffer(binaryString.length); // ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 不能直接操做,而是要经过类型数组对象或 DataView 对象来操做,它们会将缓冲区中的数据表示为特定的格式,并经过这些格式来读写缓冲区的内容。 const intArray = new Uint8Array(arrayBuffer); // Uint8Array类型化数组表示一个由8位无符号整数组成的数组。内容初始化为0。一旦创建,您可使用对象的方法或使用标准数组索引语法(即便用括号符号)引用数组中的元素。 for (let i = 0, j = binaryString.length; i < j; i++) { intArray[i] = binaryString.charCodeAt(i); } const data = [intArray]; let blob; // 经过兼容性判断,最后转换为二进制数据 try { blob = new Blob(data, { type: imgFile.type }); } catch (error) { window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if (error.name === 'TypeError' && window.BlobBuilder){ const builder = new BlobBuilder(); builder.append(arrayBuffer); blob = builder.getBlob(imgFile.type); } else { throw new Error('版本太低,不支持上传图片'); } } // blob 转 file const fileOfBlob = new File([blob], imgFile.name); // File 对象是特殊类型的 Blob,且能够用在任意的 Blob 类型的 context 中。 const formData = new FormData(); // 把要传输的数据添加到FormData()对象中 // type formData.append('type', imgFile.type); // size formData.append('size', fileOfBlob.size); // name formData.append('name', imgFile.name); // lastModifiedDate formData.append('lastModifiedDate', imgFile.lastModifiedDate); // append 文件 formData.append('file', fileOfBlob); uploadImg(formData); // 调用xhr发送数据到后台 }
五、xhr实现上传
function uploadImg (formData) { const xhr = new XMLHttpRequest(); // 进度监听 xhr.upload.addEventListener('progress', (e)=>{ console.log(e, e.loaded , e.total); // 能够利用这两个对象算出目前的传输比例 }, false); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { const result = JSON.parse(xhr.responseText); if (xhr.status === 200) { // 上传成功 console.log(result); } else { // 上传失败 } } }; xhr.open('POST', '/upload' , true); // 中间"/upload"为后台上传地址(若是须要兼容性强可使用限制的ajax库) xhr.send(formData); // 发送到后台 }
代码github访问地址:上传实例代码
经过本节内容,咱们应该完全的理解了前端上传是如何实现的,给出的代码实例虽然简单,可是已是很是核心,咱们能够经过这个版本去实现一个很是复杂的需求,譬如多图片上传,那么也就是遍历的调用transformFileToDataUrl这个方法去实现。在例如添加上拖拽文件到指定区域去上传,那么咱们只须要了解下drag对象就能够很轻松的实现了。