问题
How can I resize an image (using an HTML5 canvas element) and keep the EXIF information from the original image? I can extract EXIF info from from original image but I don't know how to copy it to the resized image.
This is how I retrieve the resized image data to send to the server-side code:
canvas.toDataURL("image/jpeg", 0.7);
For EXIF retrieval, I'm using the exif.js library.
回答1:
Working solution: ExifRestorer.js
Usage with HTML5 image resize:
function dataURItoBlob(dataURI)
{
var binary = atob(dataURI.split(',')[1]);
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}
And main code, taken as part of HTML5 resizer from this page: https://github.com/josefrichter/resize/blob/master/public/preprocess.js (but slightly modified)
var reader = new FileReader();
//reader.readAsArrayBuffer(file); //load data ... old version
reader.readAsDataURL(file); //load data ... new version
reader.onload = function (event) {
// blob stuff
//var blob = new Blob([event.target.result]); // create blob... old version
var blob = dataURItoBlob(event.target.result); // create blob...new version
window.URL = window.URL || window.webkitURL;
var blobURL = window.URL.createObjectURL(blob); // and get it's URL
// helper Image object
var image = new Image();
image.src = blobURL;
image.onload = function() {
// have to wait till it's loaded
var resized = ResizeImage(image); // send it to canvas
resized = ExifRestorer.restore(event.target.result, resized); //<= EXIF
var newinput = document.createElement("input");
newinput.type = 'hidden';
newinput.name = 'html5_images[]';
newinput.value = resized; // put result from canvas into new hidden input
form.appendChild(newinput);
};
};
回答2:
It looks my code is used in 'ExifRestorer.js'...
I've try resizing image by canvas. And I felt that resized image is bad quality. If you felt so, too, try my code. My code resizes JPEG by bilinear interpolation. Of course it doesn't lose exif.
https://github.com/hMatoba/JavaScript-MinifyJpegAsync
function post(data) {
var req = new XMLHttpRequest();
req.open("POST", "/jpeg", false);
req.setRequestHeader('Content-Type', 'image/jpeg');
req.send(data.buffer);
}
function handleFileSelect(evt) {
var files = evt.target.files;
for (var i = 0, f; f = files[i]; i++){
var reader = new FileReader();
reader.onloadend = function(e){
MinifyJpegAsync.minify(e.target.result, 1280, post);
};
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
回答3:
Canvas generates images with 20 bytes header (before jpeg data segments start). You can slice head with exif segments from original file and replace first 20 bytes in resized one.
回答4:
You can use copyExif.js.
This module is more efficient than Martin's solution and uses only Blob and ArrayBuffer without Base64 encoder/decoder.
Besides, there is no need to use exif.js if you only want to keep EXIF. Just copy the entire APP1 marker from the original JPEG to the destination canvas blob and it would just work. It is also how copyExif.js does.
Usage
demo: https://codepen.io/tonytonyjan/project/editor/XEkOkv
import copyExif from "./copyExif.js";
document.getElementById("file").onchange = async ({ target: { files } }) => {
const file = files[0],
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
ctx.drawImage(await blobToImage(file), 0, 0, canvas.width, canvas.height);
canvas.toBlob(
async blob =>
document.body.appendChild(await blobToImage(await copyExif(file, blob))),
"image/jpeg"
);
};
const blobToImage = blob => {
return new Promise(resolve => {
const reader = new FileReader(),
image = new Image();
image.onload = () => resolve(image);
reader.onload = ({ target: { result: dataURL } }) => (image.src = dataURL);
reader.readAsDataURL(blob);
});
};
来源:https://stackoverflow.com/questions/18297120/html5-resize-image-and-keep-exif-in-resized-image