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?


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)


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


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.


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.

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


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>


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.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.