在上传之前使用 HTML5调整图像的大小

我已经找到了一些不同的职位,甚至问题堆栈溢出回答这个问题。我基本上实现了与 这篇文章相同的东西。

所以我的问题是。当我上传照片时,我还需要提交表格的其余部分。这是我的 html:

<form id="uploadImageForm" enctype="multipart/form-data">
<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<input id="name" value="#{name}" />
... a few more inputs ...
</form>

以前,我不需要调整图像的大小,所以我的 javascript 看起来像这样:

window.uploadPhotos = function(url){
var data = new FormData($("form[id*='uploadImageForm']")[0]);


$.ajax({
url: url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
... handle error...
}
}
});
};

这一切都工作得很好... 现在,我需要调整图像的大小... 我如何才能取代的形式,使调整的图像是张贴,而不是上传的图像?

window.uploadPhotos = function(url){


var resizedImage;


// Read in file
var file = event.target.files[0];


// Ensure it's an image
if(file.type.match(/image.*/)) {
console.log('An image has been loaded');


// Load the image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {


// Resize the image
var canvas = document.createElement('canvas'),
max_size = 1200,
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
resizedImage = canvas.toDataURL('image/jpeg');
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(file);
}




// TODO: Need some logic here to switch out which photo is being posted...


var data = new FormData($("form[id*='uploadImageForm']")[0]);


$.ajax({
url: url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
... handle error...
}
}
});
};

我已经考虑过将文件输入移出表单,并在表单中设置一个隐藏的输入,将其值设置为调整大小后的图像的值... ... 但我想知道是否可以替换已经在表单中的图像。

201303 次浏览

这就是我最后所做的,而且效果很好。

首先,我将文件输入移到表单之外,这样它就不会被提交:

<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<form id="uploadImageForm" enctype="multipart/form-data">
<input id="name" value="#{name}" />
... a few more inputs ...
</form>

然后我改变了 uploadPhotos函数,只处理调整大小:

window.uploadPhotos = function(url){
// Read in file
var file = event.target.files[0];


// Ensure it's an image
if(file.type.match(/image.*/)) {
console.log('An image has been loaded');


// Load the image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {


// Resize the image
var canvas = document.createElement('canvas'),
max_size = 544,// TODO : pull max size from a site config
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
var resizedImage = dataURLToBlob(dataUrl);
$.event.trigger({
type: "imageResized",
blob: resizedImage,
url: dataUrl
});
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(file);
}
};

正如你所看到的,我使用 canvas.toDataURL('image/jpeg');将调整大小的图像变成 dataUrl adn,然后我调用函数 dataURLToBlob(dataUrl);将 dataUrl 变成一个 blob,然后我可以将它添加到表单中。在创建 blob 时,我触发一个自定义事件。下面是创建 blob 的函数:

/* Utility function to convert a canvas to a BLOB */
var dataURLToBlob = function(dataURL) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = parts[1];


return new Blob([raw], {type: contentType});
}


var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;


var uInt8Array = new Uint8Array(rawLength);


for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}


return new Blob([uInt8Array], {type: contentType});
}
/* End Utility function to convert a canvas to a BLOB      */

最后,这里是我的事件处理程序,它从自定义事件获取 blob,追加表单,然后提交它。

/* Handle image resized events */
$(document).on("imageResized", function (event) {
var data = new FormData($("form[id*='uploadImageForm']")[0]);
if (event.blob && event.url) {
data.append('image_data', event.blob);


$.ajax({
url: event.url,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
//handle errors...
}
});
}
});

如果有人感兴趣,我已经做了一个打印版本:

interface IResizeImageOptions {
maxSize: number;
file: File;
}
const resizeImage = (settings: IResizeImageOptions) => {
const file = settings.file;
const maxSize = settings.maxSize;
const reader = new FileReader();
const image = new Image();
const canvas = document.createElement('canvas');
const dataURItoBlob = (dataURI: string) => {
const bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
atob(dataURI.split(',')[1]) :
unescape(dataURI.split(',')[1]);
const mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
const max = bytes.length;
const ia = new Uint8Array(max);
for (var i = 0; i < max; i++) ia[i] = bytes.charCodeAt(i);
return new Blob([ia], {type:mime});
};
const resize = () => {
let width = image.width;
let height = image.height;


if (width > height) {
if (width > maxSize) {
height *= maxSize / width;
width = maxSize;
}
} else {
if (height > maxSize) {
width *= maxSize / height;
height = maxSize;
}
}


canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
let dataUrl = canvas.toDataURL('image/jpeg');
return dataURItoBlob(dataUrl);
};


return new Promise((ok, no) => {
if (!file.type.match(/image.*/)) {
no(new Error("Not an image"));
return;
}


reader.onload = (readerEvent: any) => {
image.onload = () => ok(resize());
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file);
})
};

下面是 javascript 的结果:

var resizeImage = function (settings) {
var file = settings.file;
var maxSize = settings.maxSize;
var reader = new FileReader();
var image = new Image();
var canvas = document.createElement('canvas');
var dataURItoBlob = function (dataURI) {
var bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
atob(dataURI.split(',')[1]) :
unescape(dataURI.split(',')[1]);
var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
var max = bytes.length;
var ia = new Uint8Array(max);
for (var i = 0; i < max; i++)
ia[i] = bytes.charCodeAt(i);
return new Blob([ia], { type: mime });
};
var resize = function () {
var width = image.width;
var height = image.height;
if (width > height) {
if (width > maxSize) {
height *= maxSize / width;
width = maxSize;
}
} else {
if (height > maxSize) {
width *= maxSize / height;
height = maxSize;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
return dataURItoBlob(dataUrl);
};
return new Promise(function (ok, no) {
if (!file.type.match(/image.*/)) {
no(new Error("Not an image"));
return;
}
reader.onload = function (readerEvent) {
image.onload = function () { return ok(resize()); };
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file);
});
};

用法如下:

resizeImage({
file: $image.files[0],
maxSize: 500
}).then(function (resizedImage) {
console.log("upload resized image")
}).catch(function (err) {
console.error(err);
});

或(async/await) :

const config = {
file: $image.files[0],
maxSize: 500
};
const resizedImage = await resizeImage(config)
console.log("upload resized image")

如果你们中的一些人,像我一样,遇到了方向问题,我已经将这里的解决方案与 exif 方向修复相结合

Https://gist.github.com/sagimedina/f00a57de4e211456225d3114fd10b0d0

我在没有使用文件阅读器的情况下制作了自己的版本。相反,我使用的是大多数现代浏览器都支持的 createObjectUrl

/**
* Function scaling an image from a file input to specified dimensions
* If the specified dimensions are not proportional to image dimensions the output image will be cropped at center
*
* @param file {File} Input file of a form
* @param dimensions \{\{width: number, height: number}} Dimenstions of the output image
* @returns {Promise<Blob | null>} Promise resolving to a scale image or a null if provided an invalid file type
*/
export async function scaleImageBeforeUpload(file: File, dimensions: {width: number, height: number}): Promise<Blob | null> {
// ensure the file is an image
if (!file.type.match(/image.*/)) return null;


const image = new Image();
image.src = URL.createObjectURL(file);


await new Promise<Event>((res) => image.onload = res);
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d", {alpha: true});


canvas.width = dimensions.width;
canvas.height = dimensions.height;


if (image.height <= image.width) {
const scaleProportions = canvas.height / image.height;
const scaledWidth = scaleProportions * image.width;
context.drawImage(image, (canvas.width - scaledWidth)/2, 0, scaledWidth, canvas.height);
}
else {
const scaleProportions = canvas.width / image.width;
const scaledHeight = scaleProportions * image.height;
context.drawImage(image, 0, (canvas.height - scaledHeight)/2, canvas.width, scaledHeight);
}


return new Promise((res) => canvas.toBlob(res));
}