使用FormData數據作圖片上傳: new FormData() canvas實現圖片壓縮javascript
ps: 千萬要使用append不要用set 蘋果ios有兼容問題致使數據獲取不到,須要後臺接口支持formData數據 Content-Type: multipart/form-datahtml
正確的打開方式:formdata.append('file', file)vue
錯誤的打開方式:formData.set(name,value) java
//html代碼 <div class="sendImage" slot="label"> <input type="file" name="file" accept="image/*" ref="ndImgUpload" class="upload_input" @change="imgSelectChange"> </div> //**************javascript代碼 Begin********************** //************* tool.js文件 canvas實現圖片壓縮*********** // 用於壓縮圖片的canvas let canvas = document.createElement("canvas"); let ctx = canvas.getContext('2d'); // 瓦片canvas let tCanvas = document.createElement("canvas"); let tctx = tCanvas.getContext("2d"); // /** // * ************ js圖片壓縮類***************** // */ export function compress (img) { let initSize = img.src.length; let width = img.width; let height = img.height; //若是圖片大於四百萬像素,計算壓縮比並將大小壓至400萬如下 let ratio; if ((ratio = width * height / 4000000) > 1) { ratio = Math.sqrt(ratio); width /= ratio; height /= ratio; } else { ratio = 1; } canvas.width = width; canvas.height = height; // 鋪底色 ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, canvas.width, canvas.height); //若是圖片像素大於100萬則使用瓦片繪製 let count; if ((count = width * height / 1000000) > 1) { count = ~~(Math.sqrt(count) + 1); //計算要分紅多少塊瓦片 // 計算每塊瓦片的寬和高 let nw = ~~(width / count); let nh = ~~(height / count); tCanvas.width = nw; tCanvas.height = nh; for (let i = 0; i < count; i++) { for (let j = 0; j < count; j++) { tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh); ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh); } } } else { ctx.drawImage(img, 0, 0, width, height); } //進行壓縮toDataURL:值越小,壓縮力度越大 let ndata = canvas.toDataURL('image/jpeg', 0.7); console.log('壓縮前:' + initSize); console.log('壓縮後:' + ndata.length); console.log('壓縮率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%"); tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0; return ndata; } /** * 將以base64的圖片url數據轉換爲Blob * @param urlData * 用url方式表示的base64圖片數據 */ export function convertBase64UrlToBlob (urlData) { let arr = urlData.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = window.atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type: mime}); } /** * ***********業務代碼開始************ */ import {compress, convertBase64UrlToBlob} from 'utils/tool' const UPLOAD_IMG_SIZE = 10 //圖片上傳大小不能大於10M const IMG_NEED_COMPRESS_SIZE = 0.5 //圖片大小<=0.5M,則不壓縮,直接上傳 const SELECT_IMG_QUANTITY = 9 //發送消息圖片最多選9張 // *************vue代碼 *************** methods:{ imgSelectChange (e) { this.$vux.loading.show({ text: 'Loading' }) let file = e.target setTimeout(() => { //加500ms定時器是爲了防止大質量圖片上傳會形成卡頓loading會延遲執行 this.sendImgIndex = 0 if (file.files.length > SELECT_IMG_QUANTITY) { this.$vux.loading.hide() return this.$vux.alert.show({ title: '提示', content: '最多選9張圖片', onHide () { file.value = '' } }) } for (let obj of Array.from(file.files)) { if (!/\/(?:jpeg|png|gif|jpg)/i.test(obj.type)) { this.$vux.loading.hide() return this.$vux.alert.show({ title: '提示', content: `${obj.name}不是圖片文件類型`, onHide () { file.value = '' } }) } let calcSize = obj.size / 1024 / 1024 if (calcSize > UPLOAD_IMG_SIZE) { this.$vux.loading.hide() return this.$vux.alert.show({ title: '提示', content: `圖片大小不能大於${UPLOAD_IMG_SIZE}M`, onHide () { file.value = '' } }) } } const imgLen = file.files.length this.eachSendImg(file.files, file, (data) => { //msgType 0系統 1文字 2圖片 3音頻 4視頻 5回撤 this.sendMsgHandle(2, data, () => { this.sendImgIndex === imgLen && this.$vux.loading.hide() }, () => { this.sendImgIndex === imgLen && this.$vux.loading.hide() this.$vux.alert.show({ title: '提示', content: `圖片${file.files[this.sendImgIndex - 1].name}發送消息失敗` }); }) }, (err) => { }) }, 500) }, eachSendImg (files, input, success, error) { if (!files.length) return let that = this let args = arguments that.sendImg(files[that.sendImgIndex]).then(data => { success(data) that.sendImgIndex++ files[that.sendImgIndex] ? that.eachSendImg.apply(that, args) : input.value = '' }).catch(err => { that.$vux.loading.hide() let txt_tip = err.msg === 'FILE_WRONG_SIZE' ? `圖片${files[that.sendImgIndex].name}文件過大,上傳失敗` : `圖片${files[that.sendImgIndex].name}上傳失敗`; that.$vux.alert.show({ title: '提示', content: txt_tip, onHide () { error(err) that.sendImgIndex++ if (files[that.sendImgIndex]) { that.$vux.loading.show({ text: 'Loading' }) that.eachSendImg.apply(that, args) } else { input.value = '' } } }) }) }, sendImg (file) { let formdata = new FormData(); // FormData 對象 let that = this return new Promise((resolve, reject) => { if (file.size / 1024 / 1024 <= IMG_NEED_COMPRESS_SIZE) { formdata.append('file', file) that.sendImgAjax(formdata).then((res) => { resolve(res) }).catch((err) => { reject(err) }) return; } let reader = new FileReader(); reader.onload = function () { let result = this.result; let img = new Image(); img.src = result; // 圖片加載完畢以後進行壓縮,而後上傳 if (img.complete) { let data = compress(img); let blob = convertBase64UrlToBlob(data); formdata.append("file", blob, "file_" + Date.parse(new Date()) + ".jpg"); // 文件對象 that.sendImgAjax(formdata).then((res) => { resolve(res) }).catch((err) => { reject(err) }) img = null; } else { img.onload = () => { let data = compress(img); let blob = convertBase64UrlToBlob(data); formdata.append("file", blob, "file_" + Date.parse(new Date()) + ".jpg"); // 文件對象 that.sendImgAjax(formdata).then((res) => { resolve(res) }).catch((err) => { reject(err) }) img = null; }; } } reader.readAsDataURL(file); }) }, sendImgAjax (formData) { return new Promise((resolve, reject) => { getData.common.form_uploadImg({ data: formData, sucCb (data) { resolve(data) }, failCb (err) { reject(err) } }) }) }, sendMsgHandle (type, msg, success, error) {//發送消息 if (!this.inputVal.trim() && !msg) return this.$vux.toast.text('消息內容不能爲空'); this.disabled = true this.$vux.loading.show({ text: 'Loading' }) if (this.query.groupId) { let that = this getData.doctor.group_chat_send_msg({ params: { gid: that.query.groupId, //msg{1 || 0} :若是是圖片上傳,數據轉爲字符串,包含寬高url,msgType msg: msg ? JSON.stringify(msg) : that.inputVal, type //msgType 0系統 1文字 2圖片 3音頻 4視頻 5回撤 }, sucCb (data) { msg && (data.msg = JSON.parse(data.msg)) //若是是圖片上傳,msg消息數據爲對象轉的字符串 that.data.push({ chatid: data.uid, from: data.uid, content: msg ? data.msg.url : data.msg, width: msg ? data.msg.width : 0, height: msg ? data.msg.height : 0, create_time: data.date, avatar: data.pic, name: data.nick, group_id: data.gid, msg_id: data.id, msg_type: data.type }) that.addPrevTimeData() that.setLocalData(that.query.groupId, that.data.slice(-1)) type !== 2 && that.$vux.loading.hide() that.disabled = false that.inputVal = '' success && success() }, failCb (err) { type !== 2 && that.$vux.loading.hide() that.disabled = false let overtime = err.code * 1 === 0 if (that.inGoBackError(err.code) || overtime) { type === 2 && that.$vux.loading.hide() that.$vux.alert.show({ title: '提示', content: overtime ? '發送失敗,請檢查網絡' : err.code === 'USER_NOT_IN_GROUP' ? '您已被移出羣聊' : err.msg, onHide () { !overtime && that.$router.replace({ path: '/spa/msgGroup' }) } }); } else if (type === 2) { error && error() } else { that.$vux.loading.hide() that.$vux.alert.show({ title: '提示', content: '消息發送失敗' }); } } }) } } }