Cambiar el tamaño de la imagen del lado del cliente con JavaScript antes de cargarla en el servidor

147

Estoy buscando una forma de cambiar el tamaño de una imagen del lado del cliente con JavaScript (realmente cambiar el tamaño, no solo cambiar el ancho y la altura).
Sé que es posible hacerlo en Flash, pero me gustaría evitarlo si es posible.

¿Hay algún algoritmo de código abierto en algún lugar de la web?

Geraud
fuente
Para aquellos de ustedes que aún quieren saber la respuesta a esto, pueden hacer esto, pero deberán hacer algunas llamadas ajax para el procesamiento de imágenes.
poliedro
1
'Que tenga que hacer'? Bueno, después de todo, PUEDES resolverlo en el servidor y obtener esos datos de forma transparente a través de llamadas AJAX. Pero todo el intento es hacerlo del lado del cliente, y como Jeremy señala que se PUEDE hacer. Creo que este es un gran ejemplo: github.com/rossturner/HTML5-ImageUploader
Bart
2
Con su última versión, Dropzone.js admite el cambio de tamaño de la imagen del lado del cliente antes de la carga.
user1666456

Respuestas:

112

Aquí hay una esencia que hace esto: https://gist.github.com/dcollien/312bce1270a5f511bf4a

(una versión es6 y una versión .js que se puede incluir en una etiqueta de script)

Puede usarlo de la siguiente manera:

<input type="file" id="select">
<img id="preview">
<script>
document.getElementById('select').onchange = function(evt) {
    ImageTools.resize(this.files[0], {
        width: 320, // maximum width
        height: 240 // maximum height
    }, function(blob, didItResize) {
        // didItResize will be true if it managed to resize it, otherwise false (and will return the original file as 'blob')
        document.getElementById('preview').src = window.URL.createObjectURL(blob);
        // you can also now upload this blob using an XHR.
    });
};
</script>

Incluye un montón de detección de soporte y polyfills para asegurarse de que funcione en todos los navegadores que pueda administrar.

(también ignora las imágenes gif, en caso de que estén animadas)

dcollien
fuente
2
Siento que no has recibido suficientes elogios por esto, creo que funciona muy bien y es muy fácil. ¡Gracias por compartir!
Matt
44
Hmm ... Tuve un buen éxito con esto, pero descubrí que mis imágenes (además de ser redimensionadas) también sufrieron una gran pérdida de calidad (en forma de pixelación severa), aunque se superó en la resolución correcta.
Ruben Martinez Jr.
1
hola, necesito un fragmento para usar como este, y creo que esto es muy bueno, pero ... function (blob, didItResize) {// didItResize será verdadero si logró cambiar su tamaño, de lo contrario falso (y devolverá el original archivo como 'blob') document.getElementById ('preview'). src = window.URL.createObjectURL (blob); // ahora también puedes subir este blob usando un XHR. aquí tengo un problema. ¿Cómo puedo enviar esta imagen en un formulario con solo enviar el formulario? Quiero decir, como una solicitud de publicación activada por el botón Enviar. Ya sabes, la forma habitual. Gracias por la esencia!
iedmrc
3
Para cualquier persona que tenga pérdida de calidad (remuestreo), actualice el contexto del lienzo para establecer imageSmoothingEnabledcomo verdadero` y imageSmoothingQualitypara high. En mecanografiado, ese bloque se ve así: const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height);
Sean Perkins
1
¿Hay alguna posibilidad de hacer de este un paquete npm? ¡Sería súper genial!
erksch
62

La respuesta a esto es sí: en HTML 5 puede cambiar el tamaño de las imágenes del lado del cliente utilizando el elemento de lienzo. También puede tomar los nuevos datos y enviarlos a un servidor. Ver este tutorial:

http://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/

Jeremy Usher
fuente
29
Esta es una respuesta de solo enlace y, como tal, debería ser un comentario.
Jimbo
2
canvas.mozGetAsFile ("foo.png"); es una función obsoleta
mili
12

Si estabas redimensionando antes de subir, acabo de descubrir esto http://www.plupload.com/

Hace toda la magia por ti en cualquier método imaginable.

Desafortunadamente, el cambio de tamaño HTML5 solo es compatible con el navegador Mozilla, pero puede redirigir otros navegadores a Flash y Silverlight.

¡Acabo de probarlo y funcionó con mi Android!

Estaba usando http://swfupload.org/ en flash, funciona muy bien, pero el tamaño de cambio de tamaño es muy pequeño. (no recuerdo el límite) y no vuelve a html4 cuando flash no está disponible.

ignacio
fuente
44
Es bueno cambiar el tamaño del lado del cliente cuando un usuario intenta cargar una foto de 10mb que solo se almacenará como una foto mucho más pequeña. Se cargará mucho más rápido de esta manera.
Kyle
El mismo escenario aquí, tan pronto como tenga una entrada de archivo y el usuario esté en un teléfono inteligente y tome una imagen usando la cámara, será alrededor de 10 mb en los teléfonos inteligentes modernos. Si de todos modos en el lado del servidor está cambiando el tamaño y solo almacena una versión mucho más pequeña, ahorrará una gran cantidad de datos celulares y tiempo de carga al hacer el cambio de tamaño de antemano en el cliente.
Alex
1
Tenga cuidado porque plupload es AGPL.
Alex
11

http://nodeca.github.io/pica/demo/

En el navegador moderno, puede usar el lienzo para cargar / guardar datos de imágenes. Pero debe tener en cuenta varias cosas si cambia el tamaño de la imagen en el cliente:

  1. Solo tendrá 8 bits por canal (jpeg puede tener un mejor rango dinámico, aproximadamente 12 bits). Si no sube fotos profesionales, eso no debería ser un problema.
  2. Tenga cuidado con el algoritmo de cambio de tamaño. La mayoría de los redimensionadores del lado del cliente usan matemática trivial, y el resultado es peor de lo que podría ser.
  3. Es posible que necesite enfocar la imagen reducida.
  4. Si desea reutilizar metadatos (exif y otros) del original, no olvide quitar la información del perfil de color. Porque se aplica cuando carga la imagen en el lienzo.
Vitalia
fuente
6

Quizás con la etiqueta del lienzo (aunque no es portátil). Hay un blog sobre cómo rotar una imagen con lienzo aquí , supongo que si puede rotarlo, puede cambiar su tamaño. Quizás puede ser un punto de partida.

Ver esta biblioteca también.

David V.
fuente
2
Muy útiles ejemplos. Es posible que desee agregar un fragmento o dos a su respuesta en caso de que estos enlaces se rompan alguna vez.
Trevor
4

Puede usar un marco de procesamiento de imágenes de JavaScript para el procesamiento de imágenes del lado del cliente antes de cargar la imagen en el servidor.

A continuación, utilicé MarvinJ para crear un código ejecutable basado en el ejemplo de la página siguiente: "Procesar imágenes en el lado del cliente antes de cargarlo en un servidor"

Básicamente, uso el método Marvin.scale (...) para cambiar el tamaño de la imagen. Luego, subo la imagen como un blob (usando el método image.toBlob () ). El servidor responde proporcionando una URL de la imagen recibida.

/***********************************************
 * GLOBAL VARS
 **********************************************/
var image = new MarvinImage();

/***********************************************
 * FILE CHOOSER AND UPLOAD
 **********************************************/
 $('#fileUpload').change(function (event) {
	form = new FormData();
	form.append('name', event.target.files[0].name);
	
	reader = new FileReader();
	reader.readAsDataURL(event.target.files[0]);
	
	reader.onload = function(){
		image.load(reader.result, imageLoaded);
	};
	
});

function resizeAndSendToServer(){
  $("#divServerResponse").html("uploading...");
	$.ajax({
		method: 'POST',
		url: 'https://www.marvinj.org/backoffice/imageUpload.php',
		data: form,
		enctype: 'multipart/form-data',
		contentType: false,
		processData: false,
		
	   
		success: function (resp) {
       $("#divServerResponse").html("SERVER RESPONSE (NEW IMAGE):<br/><img src='"+resp+"' style='max-width:400px'></img>");
		},
		error: function (data) {
			console.log("error:"+error);
			console.log(data);
		},
		
	});
};

/***********************************************
 * IMAGE MANIPULATION
 **********************************************/
function imageLoaded(){
  Marvin.scale(image.clone(), image, 120);
  form.append("blob", image.toBlob());
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.marvinj.org/releases/marvinj-0.8.js"></script>
<form id="form" action='/backoffice/imageUpload.php' style='margin:auto;' method='post' enctype='multipart/form-data'>
				<input type='file' id='fileUpload' class='upload' name='userfile'/>
</form><br/>
<button type="button" onclick="resizeAndSendToServer()">Resize and Send to Server</button><br/><br/>
<div id="divServerResponse">
</div>

Gabriel Ambrósio Archanjo
fuente
¡Esto se ve increíble!
pimbrouwers
2

Sí, con los navegadores modernos esto es totalmente factible. Incluso factible hasta el punto de cargar el archivo específicamente como un archivo binario que haya realizado cualquier cantidad de alteraciones en el lienzo.

http://jsfiddle.net/bo40drmv/

(esta respuesta es una mejora de la respuesta aceptada aquí )

Teniendo en cuenta capturar el proceso de envío de resultados en PHP con algo parecido a:

//File destination
$destination = "/folder/cropped_image.png";
//Get uploaded image file it's temporary name
$image_tmp_name = $_FILES["cropped_image"]["tmp_name"][0];
//Move temporary file to final destination
move_uploaded_file($image_tmp_name, $destination);

Si uno se preocupa por el punto de Vitaly, puede intentar recortar y cambiar el tamaño en el jfiddle de trabajo.

Tatarizar
fuente
0

En mi experiencia, este ejemplo ha sido la mejor solución para cargar una imagen redimensionada: https://zocada.com/compress-resize-images-javascript-browser/

Utiliza la función HTML5 Canvas.

El código es tan 'simple' como este:

compress(e) {
    const fileName = e.target.files[0].name;
    const reader = new FileReader();
    reader.readAsDataURL(e.target.files[0]);
    reader.onload = event => {
        const img = new Image();
        img.src = event.target.result;
        img.onload = () => {
                const elem = document.createElement('canvas');
                const width = Math.min(800, img.width);
                const scaleFactor = width / img.width;
                elem.width = width;
                elem.height = img.height * scaleFactor;

                const ctx = elem.getContext('2d');
                // img.width and img.height will contain the original dimensions
                ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);
                ctx.canvas.toBlob((blob) => {
                    const file = new File([blob], fileName, {
                        type: 'image/jpeg',
                        lastModified: Date.now()
                    });
                }, 'image/jpeg', 1);
            },
            reader.onerror = error => console.log(error);
    };
}

Solo hay una desventaja con esta opción, y está relacionada con la rotación de la imagen, debido a ignorar los datos EXIF. En lo que estoy trabajando en este momento. Se actualizará después de que haya terminado con eso.

Otro inconveniente es la falta de soporte para IE / Edge, en el que también estoy trabajando. Aunque hay información en el enlace de arriba. Para los dos problemas.

xarlymg89
fuente