Zmiana rozmiaru obrazu po stronie klienta za pomocą JavaScript przed przesłaniem na serwer

147

Szukam sposobu na zmianę rozmiaru obrazu po stronie klienta za pomocą JavaScript (naprawdę zmień rozmiar, a nie tylko zmień szerokość i wysokość).
Wiem, że można to zrobić we Flashu, ale chciałbym tego uniknąć, jeśli to możliwe.

Czy jest gdzieś w sieci jakiś algorytm open source?

geraud
źródło
Dla tych z Was, którzy nadal chcą znać odpowiedź na to pytanie, możecie to zrobić, ale będziecie musieli wykonać kilka wywołań Ajax w celu przetwarzania obrazu.
wielościan
1
'Musi zrobić'? W końcu MOŻESZ rozwiązać to na serwerze i uzyskać te przejrzyste dane poprzez połączenia AJAX. Ale cała próba polega na zrobieniu tego po stronie klienta i, jak wskazuje Jeremy, można to zrobić. Myślę, że to świetny przykład: github.com/rossturner/HTML5-ImageUploader
Bart
2
W najnowszej wersji Dropzone.js obsługuje zmianę rozmiaru obrazu po stronie klienta przed przesłaniem.
user1666456

Odpowiedzi:

111

Oto streszczenie, które to robi: https://gist.github.com/dcollien/312bce1270a5f511bf4a

(wersja es6 i wersja .js, które można umieścić w tagu skryptu)

Możesz go użyć w następujący sposób:

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

Zawiera kilka funkcji wykrywania wsparcia i wypełniaczy, aby upewnić się, że działa na tylu przeglądarkach, ile zdołam.

(ignoruje również obrazy GIF - na wypadek, gdyby były animowane)

dcollien
źródło
2
Czuję, że nie otrzymałeś za to wystarczającej pochwały - myślę, że działa świetnie i jest super łatwe. Dzięki za udostępnienie!
Matt
4
Hmm ... odniosłem dobry sukces, ale stwierdziłem, że moje obrazy (oprócz zmiany rozmiaru) również doznały ogromnej utraty jakości (w postaci silnej pikselizacji), chociaż były drukowane w prawidłowej rozdzielczości.
Ruben Martinez Jr.
1
cześć, potrzebuję fragmentu do użycia takiego jak ten i myślę, że to jest bardzo dobre, ale .. function (blob, didItResize) {// didItResize będzie prawdziwe, jeśli udało mu się zmienić jego rozmiar, w przeciwnym razie false (i zwróci oryginał plik jako „blob”) document.getElementById („preview”). src = window.URL.createObjectURL (blob); // możesz teraz również przesłać ten obiekt blob za pomocą XHR. tutaj mam problem .. Jak mogę wysłać to zdjęcie w formularzu z samym przesłaniem formularza. Mam na myśli, jak żądanie wpisu wywołane przyciskiem przesyłania. Wiesz, w zwykły sposób. Dziękuję za sedno!
iedmrc,
3
Dla każdego, kto ma utratę jakości (ponowne próbkowanie), zaktualizuj kontekst kanwy, ustawiając go imageSmoothingEnabledna true i imageSmoothingQualityna high. W maszynopisie blok ten wygląda następująco: const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height);
Sean Perkins
1
Czy jest szansa na zrobienie tego pakietu npm? Byłoby super!
erksch
62

Odpowiedź brzmi: tak - w HTML 5 możesz zmieniać rozmiar obrazów po stronie klienta za pomocą elementu canvas. Możesz także pobrać nowe dane i wysłać je na serwer. Zobacz ten samouczek:

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

Jeremy Usher
źródło
28
To jest odpowiedź zawierająca tylko łącze i jako taka powinna być komentarzem.
Jimbo,
2
canvas.mozGetAsFile ("foo.png"); jest przestarzałą funkcją
mili
12

Jeśli zmieniałeś rozmiar przed załadowaniem, właśnie znalazłem to http://www.plupload.com/

Robi za Ciebie całą magię w każdą możliwą metodę.

Niestety zmiana rozmiaru HTML5 jest obsługiwana tylko przez przeglądarkę Mozilla, ale możesz przekierować inne przeglądarki do Flash i Silverlight.

Właśnie go wypróbowałem i zadziałało z moim Androidem!

Używałem http://swfupload.org/ lampa błyskowa, to spełnia swoje zadanie bardzo dobrze, ale wielkość zmiany rozmiaru jest bardzo mała. (nie pamięta limitu) i nie wraca do html4, gdy flash nie jest dostępny.

ignacio
źródło
44
Miło jest zmienić rozmiar po stronie klienta, gdy użytkownik próbuje przesłać zdjęcie o rozmiarze 10 MB, które ma zostać zapisane tylko jako znacznie mniejsze zdjęcie. W ten sposób załaduje się znacznie szybciej.
Kyle
Ten sam scenariusz tutaj, gdy tylko masz plik wejściowy, a użytkownik jest na smartfonie i robi zdjęcie za pomocą aparatu, na nowoczesnych smartfonach będzie to około 10 MB. Jeśli po stronie serwera i tak zmieniasz jego rozmiar i przechowujesz tylko znacznie mniejszą wersję, zaoszczędzisz dużo danych komórkowych i czasu ładowania, wykonując wcześniej zmianę rozmiaru w kliencie.
Alex
1
Uważaj, ponieważ plupload to AGPL.
Alex
11

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

W nowoczesnej przeglądarce możesz załadować / zapisać dane obrazu za pomocą płótna. Ale powinieneś pamiętać o kilku rzeczach, jeśli zmienisz rozmiar obrazu na kliencie:

  1. Będziesz mieć tylko 8 bitów na kanał (jpeg może mieć lepszy zakres dynamiki, około 12 bitów). Jeśli nie przesyłasz profesjonalnych zdjęć, nie powinno to stanowić problemu.
  2. Uważaj na algorytm zmiany rozmiaru. Większość funkcji zmiany wielkości po stronie klienta używa trywialnej matematyki, a wynik jest gorszy niż mógłby być.
  3. Może być konieczne wyostrzenie przeskalowanego obrazu.
  4. Jeśli chcesz ponownie wykorzystać metadane (exif i inne) z oryginału - nie zapomnij usunąć informacji o profilu kolorów. Ponieważ jest stosowany podczas ładowania obrazu do płótna.
Witalij
źródło
6

Być może z tagiem Canvas (chociaż nie jest przenośny). Jest to blog o tym, jak obrócić obraz z płótna tutaj , przypuszczam, jeśli można go obracać, można go zmienić. Może to może być punkt wyjścia.

Zobacz również tę bibliotekę .

David V.
źródło
2
Bardzo przydatne przykłady. Możesz dodać fragment lub dwa do odpowiedzi na wypadek, gdyby te linki kiedykolwiek się zepsuły.
Trevor,
4

Możesz użyć struktury przetwarzania obrazu JavaScript do przetwarzania obrazu po stronie klienta przed przesłaniem obrazu na serwer.

Poniżej wykorzystałem MarvinJ do stworzenia wykonywalnego kodu na podstawie przykładu na następującej stronie: „Przetwarzanie obrazów po stronie klienta przed przesłaniem ich na serwer”

Zasadniczo używam metody Marvin.scale (...) do zmiany rozmiaru obrazu. Następnie przesyłam obraz jako blob (używając metody image.toBlob () ). Serwer odpowiada, podając adres URL otrzymanego obrazu.

/***********************************************
 * 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
źródło
To wygląda cholernie niesamowicie!
pimbrouwers
2

Tak, w nowoczesnych przeglądarkach jest to całkowicie wykonalne. Nawet wykonalne do momentu przesłania pliku specjalnie jako plik binarny po dokonaniu dowolnej liczby zmian w kanwie.

http://jsfiddle.net/bo40drmv/

(ta odpowiedź jest ulepszeniem przyjętej tutaj odpowiedzi )

Mając na uwadze, aby złapać proces przesyłania wyniku w PHP za pomocą czegoś podobnego do:

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

Jeśli martwisz się o punkt widzenia Witalija, możesz spróbować przyciąć i zmienić rozmiar działającego pliku jfiddle.

Tatarize
źródło
0

Z mojego doświadczenia wynika, że ​​ten przykład jest najlepszym rozwiązaniem do przesyłania obrazu o zmienionym rozmiarze: https://zocada.com/compress-resize-images-javascript-browser/

Używa funkcji HTML5 Canvas.

Kod jest tak „prosty” jak ten:

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

Ta opcja ma tylko jedną wadę i jest związana z obracaniem obrazu, z powodu ignorowania danych EXIF. Nad którym w tej chwili pracuję. Zaktualizuje, gdy to zrobię.

Kolejnym minusem jest brak wsparcia dla IE / Edge, nad którym też pracuję. Chociaż w powyższym linku są informacje. W obu przypadkach.

xarlymg89
źródło