Sunucuya yüklemeden önce istemci tarafı JavaScript ile yeniden boyutlandırma


150

JavaScript ile istemci tarafını yeniden boyutlandırmanın bir yolunu arıyorum (gerçekten yeniden boyutlandırmak, yalnızca genişliği ve yüksekliği değiştirmek değil).
Flash'ta yapmanın mümkün olduğunu biliyorum ama mümkünse bundan kaçınmak isterim.

Web'in herhangi bir yerinde açık kaynaklı bir algoritma var mı?


Hala bunun cevabını bilmek isteyenleriniz için, Bunu Yapabilirsiniz, ancak görüntü işleme için bazı ajax çağrıları yapmanız gerekecektir.
polyhedron

2
'Yapmak zorundayız'? Peki sonuçta bunu sunucuda çözebilir ve AJAX çağrıları aracılığıyla bu şeffaf veriyi elde edebilirsiniz. Ancak tüm girişim bunu müşteri tarafında yapmaktır ve Jeremy'nin işaret ettiği gibi, YAPILABİLİR. Bunun harika bir örnek olduğunu düşünüyorum: github.com/rossturner/HTML5-ImageUploader
Bart

2
Dropzone.js, en son sürümü ile yüklemeden önce istemci tarafında görüntü yeniden boyutlandırmayı destekler.
user1666456

Yanıtlar:


114

İşte bunu yapan bir özet: https://gist.github.com/dcollien/312bce1270a5f511bf4a

(bir es6 sürümü ve bir komut dosyası etiketine dahil edilebilen bir .js sürümü)

Aşağıdaki gibi kullanabilirsiniz:

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

Yönetebildiğim kadar çok tarayıcıda çalıştığından emin olmak için bir dizi destek algılama ve çoklu doldurma içerir.

(animasyonlu olmaları durumunda gif resimlerini de yok sayar)


2
Bunun için neredeyse yeterince övgü almadığınızı hissediyorum - bence harika çalışıyor ve çok kolay. Paylaşım için teşekkürler!
Matt

4
Hmm..Bu konuda iyi bir başarı elde ettim, ancak görüntülerimin (yeniden boyutlandırılmaya ek olarak), doğru çözünürlükte çıktısına rağmen büyük bir kalite kaybına (şiddetli pikselleşme şeklinde) maruz kaldığını gördüm.
Ruben Martinez Jr.

1
merhaba, bunun gibi kullanmak için bir pasaja ihtiyacım var ve bunun çok iyi olduğunu düşünüyorum ama .. function (blob, didItResize) {// didItResize eğer onu yeniden boyutlandırmayı başarırsa doğru olacak, aksi takdirde false (ve orijinali döndürür 'blob' olarak dosya) document.getElementById ('önizleme'). src = window.URL.createObjectURL (blob); // bu blobu artık bir XHR kullanarak da yükleyebilirsiniz. burada bir sorunum var .. Sadece formu göndererek bu resmi bir formda nasıl gönderebilirim? Demek istediğim, gönder düğmesiyle tetiklenen bir gönderi isteği gibi. Biliyorsun, her zamanki yol. Özet için teşekkürler!
iedmrc

4
Kalite kaybı (yeniden örnekleme) olan herkes için, tuval bağlamını imageSmoothingEnabledtrue` ve imageSmoothingQualityto olarak ayarlamak için güncelleyin high. Blok gibi görünen yazı tipinde: const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height);
Sean Perkins

1
Bunu bir npm paketi yapma şansı var mı? Süper harika olur!
erksch


12

Yüklemeden önce yeniden boyutlandırıyorsanız, bu http://www.plupload.com/

Akla gelebilecek herhangi bir yöntemde tüm sihri sizin için yapar.

Maalesef HTML5 yeniden boyutlandırma yalnızca Mozilla tarayıcısında desteklenir, ancak diğer tarayıcıları Flash ve Silverlight'a yeniden yönlendirebilirsiniz.

Sadece denedim ve androidimle çalıştı!

Flash'ta http://swfupload.org/ kullanıyordum , işi çok iyi yapıyor, ancak yeniden boyutlandırma boyutu çok küçük. (sınırı hatırlayamaz) ve flash mevcut olmadığında html4'e geri dönmez.


44
Bir kullanıcı yalnızca çok daha küçük bir fotoğraf olarak saklanacak olan 10mb'lik bir fotoğrafı yüklemeye çalıştığında istemci tarafında yeniden boyutlandırma yapmak güzel. Bu şekilde çok daha hızlı yüklenir.
Kyle

Buradaki aynı senaryo, bir dosya girişiniz olduğunda ve kullanıcı bir akıllı telefona girip kamerayı kullanarak bir görüntü yakaladığında, modern akıllı telefonlarda yaklaşık 10 mb olacaktır. Sunucu tarafında yine de yeniden boyutlandırıyorsanız ve yalnızca çok daha küçük bir sürümünü depoluyorsanız, istemcide yeniden boyutlandırma işlemini önceden yaparken çok fazla hücresel veri ve yükleme süresinden tasarruf edersiniz.
Alex

1
Dikkatli olun çünkü plupload AGPL'dir.
Alex

11

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

Modern tarayıcıda, görüntü verilerini yüklemek / kaydetmek için tuvali kullanabilirsiniz. Ancak istemcide görüntüyü yeniden boyutlandırırsanız birkaç şeyi aklınızda bulundurmalısınız:

  1. Kanal başına yalnızca 8 bit'e sahip olacaksınız (jpeg daha iyi dinamik aralığa sahip olabilir, yaklaşık 12 bit). Profesyonel fotoğraflar yüklemezseniz, bu sorun olmaz.
  2. Algoritmayı yeniden boyutlandırma konusunda dikkatli olun. İstemci tarafı yeniden boyutlandırıcıların çoğu önemsiz matematik kullanır ve sonuç olması gerekenden daha kötüdür.
  3. Ölçeği küçültülmüş görüntüyü netleştirmeniz gerekebilir.
  4. İsterseniz meta verileri (exif ve diğer) orijinalinden yeniden kullanmak isterseniz renk profili bilgilerini çıkarmayı unutmayın. Çünkü görüntüyü tuvale yüklediğinizde uygulanır.

6

Belki tuval etiketiyle (taşınabilir olmasa da). Burada bir resmin tuval ile nasıl döndürüleceğiyle ilgili bir blog var , sanırım onu ​​döndürebilirseniz yeniden boyutlandırabilirsiniz. Belki bir başlangıç ​​noktası olabilir.

Bu kitaplığa da bakın .


2
Çok faydalı örnekler. Bu bağlantıların kopması ihtimaline karşı cevabınıza bir veya iki pasaj eklemek isteyebilirsiniz.
Trevor

5

Görüntüyü sunucuya yüklemeden önce istemci tarafı görüntü işleme için bir javascript görüntü işleme çerçevesi kullanabilirsiniz.

Aşağıda , aşağıdaki sayfadaki örneğe göre çalıştırılabilir bir kod oluşturmak için MarvinJ'i kullandım : "Görüntüleri bir sunucuya yüklemeden önce istemci tarafında işleme"

Temel olarak , resmi yeniden boyutlandırmak için Marvin.scale (...) yöntemini kullanıyorum . Ardından, görüntüyü bir blob olarak yüklerim (image.toBlob () yöntemini kullanarak ). Sunucu, alınan görüntünün URL'sini sağlayarak yanıt verir.

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


Bu harika görünüyor!
pim

2

Evet, modern tarayıcılarda bu tamamen yapılabilir. Hatta, herhangi bir sayıda tuval değişikliği yapılmış bir ikili dosya olarak dosyayı yükleme noktasına kadar bile yapılabilir.

http://jsfiddle.net/bo40drmv/

(bu cevap, burada kabul edilen cevabın iyileştirilmesidir )

Aşağıdakine benzer bir şeyle PHP'de sonuç gönderimini yakalamayı unutmayın:

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

Biri Vitaly'nin düşüncesi hakkında endişelenirse, çalışan jfiddle üzerinde kırpmanın ve yeniden boyutlandırmanın bir kısmını deneyebilirsiniz.


2

Tecrübelerime göre, bu örnek yeniden boyutlandırılmış bir resmi yüklemek için en iyi çözüm olmuştur: https://zocada.com/compress-resize-images-javascript-browser/

HTML5 Canvas özelliğini kullanır.

Kod bu kadar 'basit':

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

Bu çözümün iki dezavantajı var.

İlki, EXIF ​​verilerinin göz ardı edilmesinden dolayı görüntü döndürme ile ilgilidir. Bu sorunu çözemedim ve kullanım durumumda çok önemli değildim, ancak herhangi bir geri bildirim duymaktan memnuniyet duyarım.

İkinci dezavantaj, IE / Edge'e karşı destek eksikliği (Chrome tabanlı sürüm değil) ve buna hiç zaman ayırmayacağım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.