Redimensionamento de imagem do lado do cliente com JavaScript antes de fazer o upload para o servidor

147

Estou procurando uma maneira de redimensionar uma imagem do lado do cliente com JavaScript (realmente redimensionar, não apenas alterar a largura e a altura).
Sei que é possível fazê-lo no Flash, mas gostaria de evitá-lo, se possível.

Existe algum algoritmo de código aberto em algum lugar da web?

geraud
fonte
Para aqueles que ainda desejam saber a resposta, você pode fazer isso, mas precisará fazer algumas chamadas de ajax para processamento de imagem.
poliedro
1
'Precisa fazer'? Afinal, você PODE resolvê-lo no servidor e obter esses dados de forma transparente através de chamadas AJAX. Mas toda a tentativa é fazer isso do lado do cliente e, como Jeremy ressalta, PODE ser feito. Acho que este é um grande exemplo: github.com/rossturner/HTML5-ImageUploader
Bart
2
Com sua versão mais recente, o Dropzone.js suporta o redimensionamento de imagem do cliente antes do upload.
user1666456

Respostas:

111

Aqui está uma essência que faz isso: https://gist.github.com/dcollien/312bce1270a5f511bf4a

(uma versão es6 e uma versão .js que podem ser incluídas em uma tag de script)

Você pode usá-lo da seguinte maneira:

<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>

Ele inclui um monte de detecção de suporte e polyfills para garantir que ele funcione no maior número de navegadores que eu possa gerenciar.

(também ignora imagens gif - caso sejam animadas)

dcollien
fonte
2
Sinto que você não recebeu elogios o suficiente por isso - acho que funciona muito bem e é super fácil. Obrigado por compartilhar!
Matt
4
Hmm ... tive um bom sucesso com isso, mas descobri que minhas imagens (além de serem redimensionadas) também sofriam uma enorme perda de qualidade (na forma de pixelização severa), embora fossem exibidas na resolução correta.
Ruben Martinez Jr.
1
oi, preciso de um trecho para usar como este, e acho que é muito bom, mas .. function (blob, didItResize) {// didItResize será verdadeiro se ele conseguir redimensioná-lo, caso contrário, falso (e retornará o original arquivo como 'blob') document.getElementById ('preview'). src = window.URL.createObjectURL (blob); // agora você também pode fazer upload deste blob usando um XHR. aqui estou com um problema .. Como posso enviar esta imagem em um formulário com apenas o envio do formulário. Quero dizer, como uma solicitação de postagem acionada pelo botão enviar. Você sabe, da maneira usual. Obrigado pela essência!
Iedmrc
3
Para qualquer pessoa com perda de qualidade (reamostragem), atualize o contexto da tela para definir imageSmoothingEnabledcomo true` e imageSmoothingQualitypara high. No texto datilografado, esse bloco se parece com: const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height);
Sean Perkins
1
Existe a chance de fazer deste um pacote npm? Seria super ótimo!
Erksch #
62

A resposta é sim - no HTML 5 você pode redimensionar imagens do lado do cliente usando o elemento canvas. Você também pode pegar os novos dados e enviá-los para um servidor. Veja este tutorial:

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

Jeremy Usher
fonte
28
Esta é uma resposta apenas para link e, como tal, deve ser um comentário.
Jimbo 06/07
2
canvas.mozGetAsFile ("foo.png"); é uma função obsoleta
mili
12

Se você estava redimensionando antes de fazer o upload, acabei de descobrir este http://www.plupload.com/

Faz toda a mágica para você em qualquer método imaginável.

Infelizmente, apenas o redimensionamento HTML5 é suportado pelo navegador Mozilla, mas você pode redirecionar outros navegadores para o Flash e o Silverlight.

Eu apenas tentei e funcionou com o meu android!

Eu estava usando http://swfupload.org/ no flash, ele faz o trabalho muito bem, mas o tamanho do redimensionamento é muito pequeno. (não se lembra do limite) e não volta para html4 quando o flash não está disponível.

ignacio
fonte
44
É bom fazer o redimensionamento do lado do cliente para quando um usuário tentar fazer upload de uma foto de 10mb que será armazenada apenas como uma foto muito menor. Será enviado muito mais rápido dessa maneira.
Kyle
O mesmo cenário aqui, assim que você tiver uma entrada de arquivo e o usuário estiver em um smartphone e capturar uma imagem usando a câmera, será de cerca de 10 mb nos smartphones modernos. Se você, no servidor, está redimensionando-o e armazenando apenas uma versão muito menor, economizará muitos dados de celular e tempo de carregamento para redimensionar antecipadamente o cliente.
Alex
1
Tenha cuidado porque o plupload é AGPL.
Alex
11

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

No navegador moderno, você pode usar a tela para carregar / salvar dados de imagem. Mas você deve ter em mente várias coisas se redimensionar a imagem no cliente:

  1. Você terá apenas 8 bits por canal (o jpeg pode ter uma melhor faixa dinâmica, cerca de 12 bits). Se você não enviar fotos profissionais, isso não deve ser um problema.
  2. Tenha cuidado com o redimensionamento do algoritmo. A maioria dos redimensionadores do lado do cliente usa matemática trivial e o resultado é pior do que poderia ser.
  3. Pode ser necessário aprimorar a imagem em escala reduzida.
  4. Se você deseja reutilizar metadados (exif e outros) do original - não esqueça de retirar as informações do perfil de cores. Porque é aplicado quando você carrega uma imagem na tela.
Vitaly
fonte
6

Talvez com a tag canvas (embora não seja portátil). Há um blog sobre como girar uma imagem com tela aqui , suponho que se você pode girá-lo, pode redimensioná-lo. Talvez possa ser um ponto de partida.

Veja esta biblioteca também.

David V.
fonte
2
Exemplos muito úteis. Você pode adicionar um trecho ou dois à sua resposta, caso esses links quebrem.
Trevor
4

Você pode usar uma estrutura de processamento de imagem javascript para processamento de imagem do lado do cliente antes de fazer upload da imagem no servidor.

Abaixo, usei o MarvinJ para criar um código executável com base no exemplo da página a seguir: "Processando imagens no lado do cliente antes de enviá-las para um servidor"

Basicamente, uso o método Marvin.scale (...) para redimensionar a imagem. Em seguida, carrego a imagem como um blob (usando o método image.toBlob () ). O servidor responde fornecendo uma URL da imagem recebida.

/***********************************************
 * 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
fonte
Isso parece incrível!
Pimbrouwers
2

Sim, com navegadores modernos, isso é totalmente factível. Mesmo factível a ponto de fazer o upload do arquivo especificamente como um arquivo binário, com várias alterações de tela.

http://jsfiddle.net/bo40drmv/

(esta resposta é uma melhoria da resposta aceita aqui )

Lembre-se de capturar o processo de envio de resultados no PHP com algo semelhante 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);

Se alguém se preocupa com o ponto de Vitaly, você pode tentar recortar e redimensionar o quebra-cabeça em funcionamento.

Tatarize
fonte
0

Na minha experiência, este exemplo foi a melhor solução para carregar uma imagem redimensionada: https://zocada.com/compress-resize-images-javascript-browser/

Ele usa o recurso Canvas do HTML5.

O código é tão 'simples' 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);
    };
}

Há apenas uma desvantagem nessa opção, e está relacionada à rotação da imagem, devido à ignorância dos dados EXIF. No qual estou trabalhando no momento. Será atualizado depois que eu terminar com isso.

Outra desvantagem é a falta de suporte ao IE / Edge, no qual estou trabalhando também. Embora haja informações no link acima. Para ambos os problemas.

xarlymg89
fonte