Thay đổi kích thước hình ảnh phía máy khách bằng JavaScript trước khi tải lên máy chủ


147

Tôi đang tìm cách thay đổi kích thước phía máy khách hình ảnh bằng JavaScript (thực sự thay đổi kích thước, không chỉ thay đổi chiều rộng và chiều cao).
Tôi biết có thể làm điều đó trong Flash nhưng tôi muốn tránh nếu có thể.

Có bất kỳ thuật toán mã nguồn mở nào đó trên web không?


Đối với những người bạn vẫn muốn biết câu trả lời cho điều này, Bạn có thể làm điều này, nhưng bạn sẽ cần thực hiện một số cuộc gọi ajax để xử lý hình ảnh.
đa diện

1
'Cần phải làm'? Rốt cuộc, bạn CÓ THỂ giải quyết nó trên máy chủ và nhận được dữ liệu trong suốt đó thông qua các cuộc gọi AJAX. Nhưng toàn bộ nỗ lực là làm phía khách hàng, và như Jeremy chỉ ra rằng CÓ THỂ được thực hiện. Tôi nghĩ rằng đây là một ví dụ tuyệt vời: github.com/rossturner/HTML5-ImageUploader
Bart

2
Với phiên bản mới nhất, Dropzone.js hỗ trợ thay đổi kích thước hình ảnh phía máy khách trước khi tải lên.
dùng1666456

Câu trả lời:


111

Đây là một ý chính thực hiện điều này: https://gist.github.com/dcollien/312bce1270a5f511bf4a

(phiên bản es6 và phiên bản .js có thể được bao gồm trong thẻ tập lệnh)

Bạn có thể sử dụng nó như sau:

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

Nó bao gồm một loạt các phát hiện hỗ trợ và polyfill để đảm bảo nó hoạt động trên nhiều trình duyệt mà tôi có thể quản lý.

(nó cũng bỏ qua hình ảnh gif - trong trường hợp chúng hoạt hình)


2
Tôi cảm thấy như bạn đã không nhận được gần như đủ lời khen ngợi cho điều này - tôi nghĩ rằng nó hoạt động rất tốt và siêu dễ dàng. Cám ơn vì đã chia sẻ!
Matt

4
Hmm..Tôi đã thành công với điều này, nhưng tôi thấy rằng hình ảnh của tôi (ngoài việc được thay đổi kích thước) cũng bị giảm chất lượng rất lớn (dưới dạng pixelation nghiêm trọng), mặc dù nó được xuất ra ở độ phân giải chính xác.
Ruben Martinez Jr.

1
xin chào, tôi cần một đoạn mã để sử dụng như đoạn này và tôi nghĩ rằng nó rất tốt nhưng .. hàm (blob, didItResize) {// didItResize sẽ đúng nếu nó thay đổi kích thước của nó, nếu không sẽ sai (và sẽ trả về bản gốc tệp dưới dạng 'blob') document.getEuityById ('preview'). src = window.URL.createObjectURL (blob); // bây giờ bạn cũng có thể tải lên blob này bằng XHR. Ở đây tôi có một vấn đề .. Làm thế nào tôi có thể gửi hình ảnh này trong một biểu mẫu chỉ bằng cách gửi biểu mẫu. Ý tôi là, giống như một yêu cầu bài được kích hoạt bằng nút gửi. Bạn biết đấy, cách thông thường. Cảm ơn bạn cho ý chính!
iedmrc

3
Đối với bất kỳ ai bị giảm chất lượng (lấy mẫu lại), hãy cập nhật bối cảnh canvas để đặt imageSmoothingEnabledthành true` và imageSmoothingQualitythành high. Trong bản đánh máy, khối trông như sau: const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height);
Sean Perkins

1
Có một cơ hội để làm cho một gói npm? Sẽ là siêu tuyệt vời!
erksch

62

Câu trả lời cho điều này là có - trong HTML 5, bạn có thể thay đổi kích thước hình ảnh phía máy khách bằng cách sử dụng phần tử canvas. Bạn cũng có thể lấy dữ liệu mới và gửi nó đến một máy chủ. Xem hướng dẫn này:

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


28
Đây là một câu trả lời chỉ liên kết và như vậy nên là một nhận xét.
Jimbo

2
canvas.mozGetAsFile ("foo.png"); là một chức năng không dùng nữa
mili

12

Nếu bạn đang thay đổi kích thước trước khi tải lên, tôi chỉ cần tìm ra http://www.plupload.com/ này

Nó làm tất cả các phép thuật cho bạn trong bất kỳ phương pháp có thể tưởng tượng.

Thật không may, thay đổi kích thước HTML5 chỉ được hỗ trợ với trình duyệt Mozilla, nhưng bạn có thể chuyển hướng các trình duyệt khác sang Flash và Silverlight.

Tôi vừa thử nó và nó hoạt động với Android của tôi!

Tôi đã sử dụng http://swfupload.org/ trong flash, nó hoạt động rất tốt, nhưng kích thước thay đổi kích thước rất nhỏ. (không thể nhớ giới hạn) và không quay lại html4 khi không có flash.


44
Thật tuyệt khi thực hiện thay đổi kích thước phía máy khách khi người dùng cố tải lên ảnh 10mb sẽ chỉ được lưu trữ dưới dạng ảnh nhỏ hơn nhiều. Nó sẽ tải lên nhanh hơn nhiều theo cách này.
Kyle

Kịch bản tương tự ở đây, ngay khi bạn có đầu vào tập tin và người dùng đang ở trên điện thoại thông minh và chụp ảnh bằng camera, nó sẽ ở khoảng 10 mb trên điện thoại thông minh hiện đại. Nếu bạn ở phía máy chủ dù sao cũng thay đổi kích thước và chỉ lưu trữ phiên bản nhỏ hơn nhiều, bạn sẽ tiết kiệm được nhiều dữ liệu di động và thời gian tải trước khi thực hiện thay đổi kích thước trong máy khách.
Alex

1
Hãy cẩn thận vì tải trọng là AGPL.
Alex

11

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

Trong trình duyệt hiện đại, bạn có thể sử dụng canvas để tải / lưu dữ liệu hình ảnh. Nhưng bạn nên ghi nhớ một số điều nếu bạn thay đổi kích thước hình ảnh trên máy khách:

  1. Bạn sẽ chỉ có 8 bit trên mỗi kênh (jpeg có thể có dải động tốt hơn, khoảng 12 bit). Nếu bạn không tải lên ảnh chuyên nghiệp, đó không phải là vấn đề.
  2. Hãy cẩn thận về thay đổi kích thước thuật toán. Hầu hết những người thay đổi phía khách hàng sử dụng toán học tầm thường, và kết quả là tồi tệ hơn nó có thể.
  3. Bạn có thể cần phải làm sắc nét hình ảnh hạ thấp.
  4. Nếu bạn muốn sử dụng lại siêu dữ liệu (exif và khác) từ bản gốc - đừng quên tước thông tin hồ sơ màu. Bởi vì nó được áp dụng khi bạn tải hình ảnh vào khung vẽ.

6

Có lẽ với thẻ canvas (mặc dù nó không di động). Có một blog về cách xoay hình ảnh bằng canvas ở đây , tôi cho rằng nếu bạn có thể xoay nó, bạn có thể thay đổi kích thước nó. Có lẽ nó có thể là một điểm khởi đầu.

Xem thư viện này cũng có.


2
Những ví dụ rất hữu ích. Bạn có thể muốn thêm một hoặc hai đoạn vào câu trả lời của mình trong trường hợp các liên kết này bị hỏng.
Trevor

4

Bạn có thể sử dụng khung xử lý hình ảnh javascript để xử lý hình ảnh phía máy khách trước khi tải hình ảnh lên máy chủ.

Dưới đây tôi đã sử dụng MarvinJ để tạo mã có thể chạy được dựa trên ví dụ trong trang sau: "Xử lý hình ảnh ở phía máy khách trước khi tải nó lên máy chủ"

Về cơ bản, tôi sử dụng phương thức Marvin.scale (...) để thay đổi kích thước hình ảnh. Sau đó, tôi tải lên hình ảnh dưới dạng blob (sử dụng phương thức image.toBlob () ). Máy chủ trả lời lại cung cấp một URL của hình ảnh nhận được.

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


Điều này có vẻ frickin tuyệt vời!
pimbrouwers

2

Vâng, với các trình duyệt hiện đại, điều này là hoàn toàn có thể làm được. Thậm chí có thể thực hiện đến mức tải lên tệp cụ thể như một tệp nhị phân đã thực hiện bất kỳ số lượng thay đổi canvas.

http://jsfiddle.net/bo40drmv/

(câu trả lời này là một cải tiến của câu trả lời được chấp nhận ở đây )

Hãy ghi nhớ để nắm bắt quy trình đệ trình kết quả trong PHP với một cái gì đó tương tự:

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

Nếu bạn lo lắng về quan điểm của Vitaly, bạn có thể thử một số cắt xén và thay đổi kích thước trên jfiddle đang hoạt động.


0

Theo kinh nghiệm của tôi, ví dụ này là giải pháp tốt nhất để tải lên hình ảnh đã thay đổi kích thước: https://zocada.com/compress-resize-images-javascript-browser/

Nó sử dụng tính năng HTML5 Canvas.

Mã này là "đơn giản" như thế này:

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

Chỉ có một nhược điểm với tùy chọn này và nó liên quan đến xoay hình ảnh, do bỏ qua dữ liệu EXIF. Hiện tại tôi đang làm việc. Sẽ cập nhật sau khi tôi hoàn thành việc đó.

Một nhược điểm khác, là thiếu kẻ thù hỗ trợ IE / Edge, mà tôi cũng đang làm việc. Mặc dù có thông tin trong liên kết trên. Đối với cả hai vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.