Usa HTML5 per ridimensionare un’immagine prima del caricamento

Ho trovato alcuni post diversi e anche domande su StackOverflow che rispondono a questa domanda. Sto praticamente implementando la stessa cosa di questo post .

Quindi ecco il mio problema. Quando carico la foto, devo anche inviare il resto del modulo. Ecco il mio html:

... a few more inputs ...

In precedenza, non avevo bisogno di ridimensionare l’immagine, quindi il mio javascript assomigliava a questo:

 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... } } }); }; 

Tutto questo ha funzionato alla perfezione … ora che ho bisogno di ridimensionare le immagini … come posso sostituire l’immagine nel modulo in modo che venga ridimensionato e non l’immagine caricata?

 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... } } }); }; 

Ho pensato di spostare il file in input dal modulo e di avere un input nascosto nel modulo in cui imposto il valore del valore dell’immagine ridimensionata … Ma mi chiedo se posso semplicemente sostituire l’immagine che è già nella forma.

Ecco cosa ho fatto e ha funzionato alla grande.

Innanzitutto ho spostato l’input del file all’esterno del modulo in modo che non venga inviato:

  
... a few more inputs ...

Poi ho cambiato la funzione uploadPhotos per gestire solo il ridimensionamento:

 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); } }; 

Come puoi vedere sto usando canvas.toDataURL('image/jpeg'); per cambiare l’immagine ridimensionata in un URL di dataUrl allora chiamo la funzione dataURLToBlob(dataUrl); per trasformare il dataUrl in un blob che posso quindi aggiungere al modulo. Quando viene creato il blob, faccio scattare un evento personalizzato. Ecco la funzione per creare il 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 */ 

Infine, ecco il mio gestore di eventi che prende il blob dall'evento personalizzato, aggiunge il modulo e quindi lo invia.

 /* 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... } }); } }); 

se sono interessato ho fatto una versione typescript:

 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); }) }; 

ed ecco il risultato 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); }); }; 

l’uso è come:

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

o ( async / await ):

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

Se alcuni di voi, come me, incontrano problemi di orientamento, ho combinato le soluzioni qui con una soluzione di orientamento exif

https://gist.github.com/SagiMedina/f00a57de4e211456225d3114fd10b0d0