一文了解H5照片上传过程
一、选择拍照或文件
html:
使用<input>
标签,
type
设为"file"
选择文件,
a
ccept
设为"image/*"
选择文件为图片类型和相机拍摄,
设置multiple
支持多选。
<div class="add-image"> <input class="file" type="file" accept="image/*" multiple @change="onfilechange"> <div class="add" > <img src="../../assets/add/icon_addphoto.png" alt> <p>添加照片</p> </div> </div>
样式:
设置opacity为0,使用自定义div覆盖于上面
.add-image{ width: 148px; height: 148px; position: relative;
.file{ position: absolute; top: 0; left: 0; width: 148px; height: 148px; opacity: 0; }
效果:
二、图片预览
vue数据驱动更新前端所展示图片
两种方式:
1、使用本地url(如果项目需要整理服务器图片地址作为表单提交,则本地url不可以使用,操作删除不便)
url.createobjecturl
方法可创建一个本地的 url 路径指向本地资源对象,下面使用该接口创建所选图片的地址并展示。
let url=window.url.createobjecturl(file) this.goods.goodsimagelist.push(url)
2、使用服务器返回路径(缺点:如果上传失败就无法显示)
上传图片请求成功后,服务器返回一个url,使用该url进行字符串拼接,然后加入goods.goodsimagelist数组。
三、图片旋转
纠正图片旋转角度,只要读取图片的 exif 旋转标志位,判断旋转角度,在画布上对图片进行旋转后,重新导出新的图片即可。
/** * 修正图片旋转角度问题 * @param {file} 原图片 * @return {promise} resolved promise 返回纠正后的新图片 */ function fiximageorientation (file) { return new promise((resolve, reject) => { // 获取图片 const img = new image(); img.src = window.url.createobjecturl(file); img.onerror = () => resolve(file); img.onload = () => { // 获取图片元数据(exif 变量是引入的 exif-js 库暴露的全局变量) exif.getdata(img, function() { // 获取图片旋转标志位 var orientation = exif.gettag(this, "orientation"); // 根据旋转角度,在画布上对图片进行旋转 if (orientation === 3 || orientation === 6 || orientation === 8) { const canvas = document.createelement("canvas"); const ctx = canvas.getcontext("2d"); switch (orientation) { case 3: // 旋转180° canvas.width = img.width; canvas.height = img.height; ctx.rotate((180 * math.pi) / 180); ctx.drawimage(img, -img.width, -img.height, img.width, img.height); break; case 6: // 旋转90° canvas.width = img.height; canvas.height = img.width; ctx.rotate((90 * math.pi) / 180); ctx.drawimage(img, 0, -img.height, img.width, img.height); break; case 8: // 旋转-90° canvas.width = img.height; canvas.height = img.width; ctx.rotate((-90 * math.pi) / 180); ctx.drawimage(img, -img.width, 0, img.width, img.height); break; } // 返回新图片 canvas.toblob(file => resolve(file), 'image/jpeg', 0.92) } else { return resolve(file); } }); }; }); }
四、图片压缩
现在手机拍照质量越来越高,拍出来的照片多达几m甚至十几m,直接上传原图不合理,容易上传失败,且后台对请求体大小有限制,后续加载图片展示也会变得慢,所以要求我们前端在上传之前进行图片的压缩。
下面函数实现了对图片的压缩,原理是在画布上绘制缩放后的图片,最终从画布导出压缩后的图片。方法中有两处可以对图片进行压缩控制:一处是控制图片的缩放比;另一处是控制导出图片的质量。
// 压缩图片 compressimage(file) { return new promise((resolve, reject) => { const img = new image(); img.src = window.url.createobjecturl(file); img.onerror = error => reject(error); img.onload = () => { const canvas = document.createelement('canvas'); const ctx = canvas.getcontext('2d'); canvas.width = math.min(img.width, 200);//控制图片大小 const radio = canvas.width / img.width; canvas.height = img.height * radio; //等比缩放 ctx.drawimage(img, 0, 0, canvas.width, canvas.height); const quality = 0.8; //控制输出图片质量 canvas.toblob(file => { let files = new window.file([file], 'file.jpg', { type: file.type }); resolve(files); }, 'image/jpeg', quality); }; }); },
这里有个要注意的点,toblob之后是一个blob对象,但是请求要求传入file文件,所以我们要将blob对象转为file
let files = new window.file([this.blob], file.name, {type: file.type})
五、图片上传
通过formdata
创建表单数据,发起 ajax post
请求即可,下面函数实现了上传文件。
// 上传图片 uploadfile(file) { return request({ method: 'post', posttype: 'file', url: '//...域名.../upload/comments', data: { file: file } }); },
export function formdata(obj) { let formdata = new formdata(); object.keys(obj).foreach(key => { let val = obj[key]; val = val == null ? '' : val; if (typeof val === 'object') { if (val instanceof window.file) { formdata.append(key, val); } else { formdata.append(key, json.stringify(val)); } } else { formdata.append(key, val); } }); return formdata; }
export function request(options) { return new promise((resolve, reject) => { let { method, url, data, params, headers = {}, withcredentials = false, // file || '' posttype = '' } = options; const xhr = new xmlhttprequest(); let senddata = null; method = (method || 'get').touppercase(); const urlparse = /\?/.test(url) ? parsestring(url) : {}; const querys = { timestamp: math.round(new date() / 1000), app_id: values.app_id, ...urlparse, ...params }; // 验签 let keys = object.keys(querys); keys.push('app_secret'); const api_sign = sha1(keys.sort().map(key => querys[key] || values[key]).join('')); // console.log('api_sign', api_sign); headers.api_sign = api_sign; url += (/\?/.test(url) ? '&' : '?') + object.keys(querys) .map(key => `${key}=${escape(querys[key])}`) .join('&'); xhr.open(method, url, true); // 处理senddata // posttype file if (method === 'post') { if (posttype === 'file') { senddata = data ? formdata(data) : null; } else { headers['content-type'] = headers['content-type'] || 'application/json;charset=utf-8'; senddata = data ? json.stringify(data) : null; } } object.keys(headers).foreach(key => { xhr.setrequestheader(key, headers[key]); }); xhr.onreadystatechange = function() { if (xhr.readystate === 4 && xhr.status === 200) { // options.success(xhr.responsetext); let response = { status: xhr.status, data: {} }; try { response.data = json.parse(xhr.responsetext); } catch (e) { console.warn('request error:', e); } if (response) { resolve(response); } else { reject(new error('cancel by response interceptor')); } } }; xhr.onerror = reject; // withcredentials默认为true xhr.withcredentials = withcredentials; // console.log(url, headers, senddata); xhr.send(senddata); }); }
六、合并上传
onfilechange(e) { const files = array.prototype.slice.call(e.target.files); files.foreach(file => { // 本地预览 // let url=window.url.createobjecturl(file) // this.photo.push(url) this.compressimage(file) .then(file => { return this.uploadfile(file); }).then(data => { let goodsimage = data.data.data; this.goods.goodsimagelist.push(goodsimage); console.log('上传成功'); // console.log(this.goods.goodsimagelist); }).catch(error => { console.log('上传失败'); }); }); },
最终效果:
上一篇: Oracle完全复制表结构的存储过程
下一篇: 高校市场产品的暴利及营销推广方案