nodejs端
可以安装multer插件,npm install multer
var express = require("express");
var multer = require('multer');
var _ = require('underscore')
var app = express();
var done = false;
/*配置允许ajax跨域*/
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
app.use(multer({
dest: '../public/images/uploads/',
rename: function (fieldname, filename) {
return filename + Date.now();
},
onFileUploadStart: function (file) {
console.log(file.originalname + ' is starting ...')
},
onFileUploadComplete: function (file) {
console.log(file.fieldname + ' uploaded to ' + file.path)
done = true;
}
}));
/*Handling routes.*/
app.get('/', function (req, res) {
//输出上传页面
res.sendfile("upload.html");
});
app.post('/api/upload', function (req, res) {
if (done == true) {
console.log(req.files);
res.end(_.size(req.files)+" File uploaded.");
}
});
/*Run the server.*/
app.listen(3000, function () {
console.log("Working on port 3000");
});
前端
upload.html
<input id='upload' name="pic1" type="file" onchange="change(this)" multiple/>
<button id='add'>add</button>
<button id='submit' onclick="sub()">提交啊</button>
ajax
利用xhr.send(FormData)来实现
var send2 = function(url, files) {
var xhr = new XMLHttpRequest;
xhr.open("POST", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
console.log('formdata', xhr.responseText);
}
};
var fd = new FormData;
for (var i = 0; i < files.length; i++) {
//如果多张图片文件名一样,需要设置不同name
var name = files[i].name + i;
fd.append(name , files[i])
}
xhr.send(fd);
};
利用拼接原始包的方式实现
def('upload', function () {
'use strict';
var exports = {};
var prefix = '------';
var _boundary = "WebKitFormBoundaryLfG8DWAstG9WjKAt";//默认boundary
var boundaryWithPrefix = prefix + _boundary;
exports.genBinString = function (data, files) {
var CRLF = '\r\n';
var genData = function (name, value) {
value = value || '';
return boundaryWithPrefix + CRLF +
"Content-Disposition: form-data; name=\"" + name + "\"" + CRLF + CRLF + value;
}
var genFile = function (name, fileName, type, bin) {
return boundaryWithPrefix + CRLF + 'Content-Disposition: form-data; name="' + name +'"; filename="' + fileName + '"' + CRLF + 'Content-Type: ' + type + CRLF + CRLF + bin;
};
var resultBin = '';
//resultBin += CRLF;
for (var key in data) {
resultBin += genData(key, data[key]) + CRLF;
}
//file
for (var i = 0; i < files.length; i++) {
var file = files[i];
var type = exports.getMimeType(file.src);
var bin = exports.getByteString(exports.compress(file.src));
resultBin += genFile('images', file.name, type, bin) + CRLF;
}
resultBin += boundaryWithPrefix + '--' + CRLF;
return resultBin;
}
//兼容 end
exports.send = function (option) {
var url = option.url, data = option.data, files = option.files, srcs = option.srcs,
sucFn = option.sucFn, failFn = option.failFn,
uploadStart = option.uploadStart, uploadProgress = option.uploadProgress, uploadSuccess = option.uploadSuccess, uploadError = option.uploadError;
var xhr = new XMLHttpRequest;
var upload = xhr.upload;
//xhr.onprogress = updateProgress;//下载进度
//上传进度
upload.addEventListener("loadstart", uploadStart, false);
upload.addEventListener("progress", uploadProgress, false);
upload.addEventListener("load", uploadSuccess, false);
upload.addEventListener("error", uploadError, false);
xhr.open("POST", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status == 200) {
sucFn(JSON.parse(xhr.responseText));
}
};
xhr.onerror = failFn;
xhr.withCredentials = true;
var fd = new FormData;
//data
Object.keys(data).forEach(function (key) {
fd.append(key, data[key])
})
//files
var compressedDataURIs = [];
//递归压缩图片
function compressFiles(files, idx, onComplete) {
if (!files) {
return;
}
var dataUri = files[idx].src;
if (dataUri) {
var next = function (curCompressURI) {
//压缩包含base64头,去掉
//log('压缩后:' + curCompressURI)
curCompressURI = curCompressURI.split(',')[1];
compressedDataURIs[idx] = curCompressURI;
if (idx == files.length - 1) {
return onComplete(compressedDataURIs);
}
compressFiles(files, idx + 1, onComplete);
}
exports.compress(dataUri, 80, next);
}
}
function uncompressFiles(files) {
if (files && files.length) {
files.forEach(function (file) {
fd.append('images', file);
})
}
};
//很多浏览器压缩没有效果,暂时去掉。
uncompressFiles(files);
xhr.send(fd);
//递归方式start
/* if (files && files.length) {
compressFiles(srcs, 0, function (compressedArr) {
//所有图片已压缩
if (compressedArr.length == 0 || compressedArr.join('') == '') {
uncompressFiles(files);
} else {
fd.append('base64files', JSON.stringify(compressedArr));
}
xhr.send(fd);
})
} else {
xhr.send(fd);
}*/
//递归方式end
/* srcs.forEach(function (file, idx) {
var dataUri = file.src;
//gif图,不压缩
var type = exports.getMimeType(dataUri);
if (type.indexOf('gif') > -1) {
var compressDataURI = dataUri;
} else {
//var compressDataURI = exports.compress(dataUri, 80);
//已经压缩过;
var compressDataURI = dataUri;
}
compressDataURI = compressDataURI.split(',')[1];
compressedDataURIs.push(compressDataURI);
//var imageFile = new File([imageBlob], 'images');
});
//压缩失败则原图上传
if (compressedDataURIs.join('').length == 0) {
files.forEach(function (file) {
fd.append('images', file);
})
} else {
fd.append('base64files', JSON.stringify(compressedDataURIs));
}*/
//xhr.send(fd);
//xhr.send(exports.genBinString(data, files));
}
//source:dataURI or img; img url保证base64;
exports.compress = function (source, quality, onCompressed) {
var sourceImg = new Image();
sourceImg.onload = function () {
var cvs = document.createElement('canvas')
var w = sourceImg.naturalWidth;
var h = sourceImg.naturalHeight;
var ratio = Math.max(w, h) / 1024;
ratio = Math.max(1, ratio);
cvs.width = w / ratio;
cvs.height = h / ratio;
var ctx = cvs.getContext("2d").drawImage(sourceImg, 0, 0, cvs.width, cvs.height);
var mime_type = sourceImg.src.split(',')[0].split(':')[1].split(';')[0];
var compressedDataURI = cvs.toDataURL(mime_type, quality / 100);
//log('压缩前:' + source.length + '压缩后:' + compressedDataURI.length);
onCompressed && onCompressed(compressedDataURI);
}
sourceImg.src = source;
//return compressedDataURI;
}
//dataURI to Blob;
exports.dataURItoBlob = function (dataURI) {
var byteString = exports.getByteString(dataURI);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = null;
try {
blob = new Blob([ia], {type: mimeString});
} catch (e) {
//see http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if (e.name == 'TypeError' && window.BlobBuilder) {
alert('blobbuilder')
var bb = new BlobBuilder();
bb.append([ia.buffer]);
blob = bb.getBlob(mimeString);
}
else if (e.name == "InvalidStateError") {
// InvalidStateError (tested on FF13 WinXP)
blob = new Blob([ia.buffer], {type: mimeString});
}
else {
// We're screwed, blob constructor unsupported entirely
alert('此浏览器暂不支持上传!')
}
}
return blob;
};
//convert base64/URLEncoded data component to raw binary data held in a string
exports.getByteString = function (dataURI) {
var byteString = '';
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
return byteString;
}
exports.getMimeType = function (dataURI) {
return dataURI.split(',')[0].split(':')[1].split(';')[0];
}
return exports;
})
##注:
1.问:图片压缩在iphone和一些android手机上并没有多大效果,不知道问题在哪,pc上模拟mobile确是好的
2.问:图片上传的upload。progress事件在ios6上好像没有作用。
3 扩展:压缩时可以等比缩小图片以减少上传流量,如width或height大于1024px,则按照1024的比例缩小