之前就看到H5使用canvas就可以在前端使用JS压缩图片,这次接到任务要把这个功能嵌入到ueditor里面去,以节省流量,减轻服务器压力。

H5使用canvas进行压缩的代码有很多,核心原理就是创建Image对象,导入canvas,限制canvas大小,然后导出为base64,生成二进制对象,最后将二进制对象进行上传。

我们要修改的文件在ueditor.all.js里,第24352行:

UE.plugin.register('simpleupload', function (){
var me = this,
isLoaded = false,
containerBtn;

function initUploadBtn(){
var timestrap = (+new Date()).toString(36),
doc = containerBtn.ownerDocument,
wrapper = document.createElement('div');

wrapper.innerHTML = '<form id="edui_form_' + timestrap + '" target="edui_iframe_' + timestrap + '" method="POST" enctype="multipart/form-data" action="' + me.getOpt('serverUrl') + '" ' +
'style="display:block;width:100%;height:100%;border:0;margin:0;padding:0;position:absolute;">' +
'<input id="edui_input_' + timestrap + '" type="file" accept="image/*" name="' + me.options.imageFieldName + '" ' +
'style="background:red;display:block;width:100%;height:100%;border:0;margin:0;padding:0;position:absolute;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;">' +
'</form>' +
'<iframe id="edui_iframe_' + timestrap + '" name="edui_iframe_' + timestrap + '" ' +
'style="display:none;width:0;height:0;border:0;margin:0;padding:0;position:absolute;"></iframe>';

wrapper.className = 'edui-' + me.options.theme;
wrapper.id = me.ui.id + '_iframeupload';
containerBtn.appendChild(wrapper);

var form = doc.getElementById('edui_form_' + timestrap);
var input = doc.getElementById('edui_input_' + timestrap);
var iframe = doc.getElementById('edui_iframe_' + timestrap);

domUtils.on(input, 'change', function(){
if(!input.value) return;
var loadingId = 'loading_' + (+new Date()).toString(36);
var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';

var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
var allowFiles = me.getOpt('imageAllowFiles');

me.focus();
me.execCommand('inserthtml', '<img class="loadingclass" id="' + loadingId + '" src="' + me.options.themePath + me.options.theme +'/images/spacer.gif" title="' + (me.getLang('simpleupload.loading') || '') + '" >');

function callback(){
try{
var link, json, loader,
body = (iframe.contentDocument || iframe.contentWindow.document).body,
result = body.innerText || body.textContent || '';
json = (new Function("return " + result))();
link = me.options.imageUrlPrefix + json.url;
if(json.state == 'SUCCESS' && json.url) {
loader = me.document.getElementById(loadingId);
loader.setAttribute('src', link);
loader.setAttribute('_src', link);
loader.setAttribute('title', json.title || '');
loader.setAttribute('alt', json.original || '');
loader.removeAttribute('id');
domUtils.removeClasses(loader, 'loadingclass');
} else {
showErrorLoader && showErrorLoader(json.state);
}
}catch(er){
showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
}
form.reset();
domUtils.un(iframe, 'load', callback);
}
function showErrorLoader(title){
if(loadingId) {
var loader = me.document.getElementById(loadingId);
domUtils.removeClasses(loader, 'loadingclass');
domUtils.addClass(loader, 'loaderrorclass');
loader.setAttribute('title', title || '');
}
}

// 判断文件格式是否错误
var filename = input.value,
fileext = filename ? filename.substr(filename.lastIndexOf('.')):'';
if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
return;
}

domUtils.on(iframe, 'load', callback);
form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params);
form.submit();
});

var stateTimer;
me.addListener('selectionchange', function () {
clearTimeout(stateTimer);
stateTimer = setTimeout(function() {
var state = me.queryCommandState('simpleupload');
if (state == -1) {
input.disabled = 'disabled';
} else {
input.disabled = false;
}
}, 400);
});
isLoaded = true;
}

return {
bindEvents:{
'ready': function() {
//设置loading的样式
utils.cssRule('loading',
'.loadingclass{display:inline-block;cursor:default;background: url(\''
+ this.options.themePath
+ this.options.theme +'/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' +
'.loaderrorclass{display:inline-block;cursor:default;background: url(\''
+ this.options.themePath
+ this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
'}',
this.document);
},
/* 初始化简单上传按钮 */
'simpleuploadbtnready': function(type, container) {
containerBtn = container;
me.afterConfigReady(initUploadBtn);
}
},
outputRule: function(root){
utils.each(root.getNodesByTagName('img'),function(n){
if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
n.parentNode.removeChild(n);
}
});
},
commands: {
'simpleupload': {
queryCommandState: function () {
return isLoaded ? 0:-1;
}
}
}
}
});

这段代码是创建一个form表单包裹原先的input file图片上传文件,然后上传至服务器,上传完成后进行相应的更改。

我们需要修改的地方在文件上传之前,即修改  form.submit();删除这段代码,添加如下代码:

w.files[0] = function () {
//这里开始进行图片压缩

/**
* 获取formdata
*/
function getFormData() {
var isNeedShim = ~navigator.userAgent.indexOf('Android')
&& ~navigator.vendor.indexOf('Google')
&& !~navigator.userAgent.indexOf('Chrome')
&& navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() <= 534;

return isNeedShim ? new FormDataShim() : new FormData()
}

/**
* formdata 补丁, 给不支持formdata上传blob的android机打补丁
* constructor
*/
function FormDataShim() {
console.warn('using formdata shim');

var o = this,
parts = [],
boundary = Array(21).join('-') + (+new Date() * (1e16 * Math.random())).toString(36),
oldSend = XMLHttpRequest.prototype.send;

this.append = function (name, value, filename) {
parts.push('--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"');

if (value instanceof Blob) {
parts.push('; filename="' + (filename || 'blob') + '"\r\nContent-Type: ' + value.type + '\r\n\r\n');
parts.push(value);
}
else {
parts.push('\r\n\r\n' + value);
}
parts.push('\r\n');
};

// Override XHR send()
XMLHttpRequest.prototype.send = function (val) {
var fr,
data,
oXHR = this;

if (val === o) {
// Append the final boundary string
parts.push('--' + boundary + '--\r\n');

// Create the blob
data = getBlob(parts);

// Set up and read the blob into an array to be sent
fr = new FileReader();
fr.onload = function () {
oldSend.call(oXHR, fr.result);
};
fr.onerror = function (err) {
throw err;
};
fr.readAsArrayBuffer(data);

// Set the multipart content type and boudary
this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
XMLHttpRequest.prototype.send = oldSend;
}
else {
oldSend.call(this, val);
}
};
}

/**
* 获取blob对象的兼容性写法
* param buffer
* param format
* returns {*}
*/
function getBlob(buffer, format) {
try {
return new Blob(buffer, { type: format });
} catch (e) {
var bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder);
buffer.forEach(function (buf) {
bb.append(buf);
});
return bb.getBlob(format);
}
}

var blob = URL.createObjectURL(w.files[0]);
var img = new Image();
img.src = blob;
var obj = w.files[0];

img.onload = function () {


var _this = img;

//生成比例
var _w = _this.width,
h = _this.height;
if (_w > 1024) {
var rate = _w / 1024;
_w = 1024;
h = h / rate;
}
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.setAttribute('width', _w);
canvas.setAttribute('height', h);
ctx.drawImage(_this, 0, 0, _w, h);

/**
* 生成base64
* 兼容修复移动设备需要引入mobileBUGFix.js
*/
var base64 = canvas.toDataURL('image/jpeg', obj.quality || 0.8);

// 修复IOS
if (navigator.userAgent.match(/iphone/i)) {
var mpImg = new MegaPixImage(img);
mpImg.render(canvas, { maxWidth: _w, maxHeight: h, quality: obj.quality || 0.8, orientation: 6 });
base64 = canvas.toDataURL('image/jpeg', obj.quality || 0.8);
}

// 修复android
if (navigator.userAgent.match(/Android/i)) {
var encoder = new JPEGEncoder();
base64 = encoder.encode(ctx.getImageData(0, 0, _w, h), obj.quality * 100 || 80);
}

// 生成结果
var result = {
blob: blob,
base64: base64,
clearBase64: base64.substr(base64.indexOf(',') + 1)
};
// 执行后函数
//开始通过XHR发送
var xhr = new XMLHttpRequest();
var formdata = getFormData();

//base64转二进制
var text = window.atob(base64.split(",")[1]);
var buffer = new Uint8Array(text.length);
var pecent = 0, loop = null;

for (var i = 0; i < text.length; i++) {
buffer[i] = text.charCodeAt(i);
}

var _blob = getBlob([buffer], "image/jpeg");
///
formdata.append('upfile', _blob, 'upfile.jpg');

xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
clearInterval(loop);
try {
var d, g, k, h = (r.contentDocument || r.contentWindow.document).body;
g = JSON.parse(xhr.responseText);
d = c.options.imageUrlPrefix + g.url;
"SUCCESS" == g.state && g.url ? (k = c.document.getElementById(e),
k.setAttribute("src", d),
k.setAttribute("_src", d),
k.setAttribute("title", g.title || ""),
k.setAttribute("alt", g.original || ""),
k.removeAttribute("id"),
f.removeClasses(k, "loadingclass")) : b && b(g.state)
} catch (n) {
b && b(c.getLang("simpleupload.loadError"))
}
t.reset();
f.un(r, "load", a)
}

};

xhr.open('post', 'ueditor/php/controller.php?action=uploadimage');//这里写你之前在ueditor里设置的地址
xhr.send(formdata);


//发送结束;
var _image = new Image();
_image.src = base64;
return _image;
}
//图片压缩结束
}();

图片压缩算法可以再网上换成相关其他算法。