Te recomendamos que revises esta respuesta en un entorno controlado antes de enviarlo a producción, un saludo.
Solución:
En breve:
- Lea los archivos utilizando la API FileReader HTML5 con .readAsArrayBuffer
- Cree un Blob con los datos del archivo y obtenga su URL con window.URL.createObjectURL (blob)
- Cree un nuevo elemento de imagen y establezca su src en la URL del blob del archivo
- Envía la imagen al lienzo. El tamaño del lienzo se establece en el tamaño de salida deseado
- Obtenga los datos reducidos del lienzo a través de canvas.toDataURL (“image / jpeg”, 0.7) (establezca su propio formato y calidad de salida)
- Adjunte nuevas entradas ocultas al formulario original y transfiera las imágenes dataURI básicamente como texto normal
- En el backend, lea el dataURI, decodifique desde Base64 y guárdelo
Código fuente.
Veo dos cosas que faltan en las otras respuestas:
canvas.toBlob
(cuando esté disponible) es más eficaz quecanvas.toDataURL
y también async.- el archivo -> imagen -> lienzo -> conversión de archivo pierde datos EXIF; en particular, los datos sobre la rotación de imágenes establecidos comúnmente por teléfonos / tabletas modernos.
El siguiente guión trata de ambos puntos:
// From https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob, needed for Safari:
if (!HTMLCanvasElement.prototype.toBlob)
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob',
value: function(callback, type, quality)
var binStr = atob(this.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++)
arr[i] = binStr.charCodeAt(i);
callback(new Blob([arr], 'image/png'));
);
window.URL = window.URL || window.webkitURL;
// Modified from https://stackoverflow.com/a/32490603, cc by-sa 3.0
// -2 = not jpeg, -1 = no data, 1..8 = orientations
function getExifOrientation(file, callback)
// Suggestion from http://code.flickr.net/2012/06/01/parsing-exif-client-side-using-javascript-2/:
if (file.slice)
file = file.slice(0, 131072);
else if (file.webkitSlice)
file = file.webkitSlice(0, 131072);
var reader = new FileReader();
reader.onload = function(e)
var view = new DataView(e.target.result);
if (view.getUint16(0, false) != 0xFFD8)
callback(-2);
return;
var length = view.byteLength, offset = 2;
while (offset < length)
var marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1)
if (view.getUint32(offset += 2, false) != 0x45786966)
callback(-1);
return;
var little = view.getUint16(offset += 6, false) == 0x4949;
offset += view.getUint32(offset + 4, little);
var tags = view.getUint16(offset, little);
offset += 2;
for (var i = 0; i < tags; i++)
if (view.getUint16(offset + (i * 12), little) == 0x0112)
callback(view.getUint16(offset + (i * 12) + 8, little));
return;
else if ((marker & 0xFF00) != 0xFF00) break;
else offset += view.getUint16(offset, false);
callback(-1);
;
reader.readAsArrayBuffer(file);
// Derived from https://stackoverflow.com/a/40867559, cc by-sa
function imgToCanvasWithOrientation(img, rawWidth, rawHeight, orientation)
var canvas = document.createElement('canvas');
if (orientation > 4)
canvas.width = rawHeight;
canvas.height = rawWidth;
else
canvas.width = rawWidth;
canvas.height = rawHeight;
if (orientation > 1)
console.log("EXIF orientation = " + orientation + ", rotating picture");
var ctx = canvas.getContext('2d');
switch (orientation)
case 2: ctx.transform(-1, 0, 0, 1, rawWidth, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, rawWidth, rawHeight); break;
case 4: ctx.transform(1, 0, 0, -1, 0, rawHeight); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, rawHeight, 0); break;
case 7: ctx.transform(0, -1, -1, 0, rawHeight, rawWidth); break;
case 8: ctx.transform(0, -1, 1, 0, 0, rawWidth); break;
ctx.drawImage(img, 0, 0, rawWidth, rawHeight);
return canvas;
function reduceFileSize(file, acceptFileSize, maxWidth, maxHeight, quality, callback)
if (file.size <= acceptFileSize)
callback(file);
return;
var img = new Image();
img.onerror = function()
URL.revokeObjectURL(this.src);
callback(file);
;
img.onload = function()
URL.revokeObjectURL(this.src);
getExifOrientation(file, function(orientation)
var w = img.width, h = img.height;
var scale = (orientation > 4 ?
Math.min(maxHeight / w, maxWidth / h, 1) :
Math.min(maxWidth / w, maxHeight / h, 1));
h = Math.round(h * scale);
w = Math.round(w * scale);
var canvas = imgToCanvasWithOrientation(img, w, h, orientation);
canvas.toBlob(function(blob)
console.log("Resized image to " + w + "x" + h + ", " + (blob.size >> 10) + "kB");
callback(blob);
, 'image/jpeg', quality);
);
;
img.src = URL.createObjectURL(file);
Uso de ejemplo:
inputfile.onchange = function()
// If file size > 500kB, resize such that width <= 1000, quality = 0.9
reduceFileSize(this.files[0], 500*1024, 1000, Infinity, 0.9, blob => );
;
La respuesta de @PsychoWoods es buena. Me gustaría ofrecer mi propia solución. Esta función de Javascript toma una URL de datos de imagen y un ancho, la escala al nuevo ancho y devuelve una nueva URL de datos.
// Take an image URL, downscale it to the given width, and return a new image URL.
function downscaleImage(dataUrl, newWidth, imageType, imageArguments) "image/jpeg";
imageArguments = imageArguments
Este código se puede utilizar en cualquier lugar donde tenga una URL de datos y desee una URL de datos para una imagen reducida.
Recuerda que puedes comunicar esta crónica si te fue útil.
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)