在上传到服务器之前,使用 JavaScript 调整客户端图像大小

Image resizing client-side with JavaScript before upload to the server

提问人:geraud 提问时间:3/13/2010 最后编辑:Adrianogeraud 更新时间:11/11/2020 访问量:200793

问:

我正在寻找一种使用 JavaScript 在客户端调整图像大小的方法(真正调整大小,而不仅仅是更改宽度和高度)。
我知道在Flash中可以做到这一点,但如果可能的话,我想避免它。

网络上有没有开源算法?

JavaScript 图像 调整客户端大小

评论

0赞 polyhedron 10/24/2010
对于那些仍然想知道这个问题答案的人,你可以这样做,但你需要进行一些ajax调用来进行图像处理。
3赞 Bart 1/27/2016
“需要制作”?好吧,毕竟您可以在服务器上解决它,并通过 AJAX 调用获取透明的数据。但整个尝试是在客户端进行,正如 Jeremy 指出的那样,可以做到。我认为这是一个很好的例子:github.com/rossturner/HTML5-ImageUploader
2赞 user1666456 7/17/2017
凭借其最新版本,Dropzone.js 支持在上传前调整客户端图像大小。

答:

6赞 David V. 3/13/2010 #1

也许带有 canvas 标签(尽管它不是便携式的)。这里有一个关于如何使用画布旋转图像的博客,我想如果你可以旋转它,你可以调整它的大小。也许它可以成为一个起点。

另请参阅此库

评论

2赞 Trevor 4/7/2014
非常有用的例子。您可能需要在答案中添加一两个片段,以防这些链接中断。
63赞 Jeremy Usher 4/21/2011 #2

答案是肯定的 - 在 HTML 5 中,您可以使用 canvas 元素在客户端调整图像大小。您还可以获取新数据并将其发送到服务器。请参阅此教程:

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

评论

39赞 Jimbo 7/6/2016
这是一个仅链接的答案,因此应该是一个评论。
2赞 mili 11/8/2018
canvas.mozGetAsFile(“foo.png”);是已弃用的函数
12赞 ignacio 8/23/2011 #3

如果您在上传之前调整大小,我刚刚发现了这个 http://www.plupload.com/

它以任何可以想象的方法为您完成所有魔术。

不幸的是,Mozilla浏览器仅支持HTML5调整大小,但您可以将其他浏览器重定向到Flash和Silverlight。

我刚刚尝试过,它适用于我的 android!

我在闪存中使用 http://swfupload.org/,它做得很好,但调整大小非常小。(不记得限制),并且在 Flash 不可用时不会返回 HTML4。

评论

47赞 Kyle 10/10/2013
当用户尝试上传一张 10mb 的照片时,最好进行客户端调整大小,而该照片只会存储为一张小得多的照片。这样上传速度会快得多。
0赞 Alex 8/1/2016
同样的情况是,只要您有文件输入并且用户在智能手机上并使用相机拍摄图像,现代智能手机上的大小约为 10 mb。如果您在服务器端无论如何都在调整它的大小并且只存储它的较小版本,那么您将节省大量的蜂窝数据和加载时间,以便在客户端中预先调整大小。
2赞 Alex 1/27/2017
要小心,因为 plupload 是 AGPL。
127赞 dcollien 7/28/2015 #4

这里有一个要点,可以做到这一点:https://gist.github.com/dcollien/312bce1270a5f511bf4a

(ES6 版本和可包含在脚本标记中的 .js 版本)

您可以按如下方式使用它:

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

它包括一堆支持检测和 polyfills,以确保它可以在我能管理的尽可能多的浏览器上运行。

(它还会忽略 GIF 图像 - 以防它们被动画化)

评论

2赞 Matt 11/5/2015
我觉得你还没有得到足够的赞誉——我认为它效果很好,而且超级容易。感谢分享!
4赞 Ruben Martinez Jr. 12/4/2015
嗯。。我在这方面取得了很好的成功,但我发现我的图像(除了调整大小之外)也遭受了巨大的质量损失(以严重的像素化形式),尽管它以正确的分辨率输出。
1赞 iedmrc 8/10/2016
嗨,我需要一个像这样的片段来使用,我认为这很好,但是..function(blob, didItResize) { // didItResize 如果设法调整大小,则为 true,否则为 false(并将原始文件返回为 'blob') document.getElementById('preview').src = window。URL.createObjectURL(blob);现在还可以使用 XHR 上传此 Blob。在这里我有一个问题..我怎样才能在表格中发送此图像,只需提交表格即可。我的意思是,就像提交按钮触发的帖子请求一样。你知道,通常的方式。谢谢你的要点!
5赞 Sean Perkins 11/22/2017
对于质量损失(重新采样)的任何人,请将画布上下文更新为设置为 true' 和 。在打字稿中,该块如下所示:imageSmoothingEnabledimageSmoothingQualityhighconst ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height);
1赞 erksch 5/2/2019
有没有机会把它做成一个 npm 包?会超级棒!
11赞 Vitaly 4/21/2016 #5

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

在现代浏览器中,您可以使用画布来加载/保存图像数据。但是,如果您在客户端上调整图像大小,您应该记住几件事:

  1. 每个通道只有 8 位(jpeg 可以有更好的动态范围,大约 12 位)。如果您不上传专业照片,那应该不是问题。
  2. 注意调整大小算法。大多数客户端调整器都使用琐碎的数学运算,结果比可能更糟糕。
  3. 您可能需要锐化缩小的图像。
  4. 如果您愿意,请重用原始元数据(exif 和其他) - 不要忘记剥离颜色配置文件信息。因为它是在将图像加载到画布时应用的。
2赞 Tatarize 6/12/2016 #6

是的,使用现代浏览器,这是完全可行的。甚至可以将文件专门上传为二进制文件,并进行了任意数量的画布更改。

http://jsfiddle.net/bo40drmv/

(这个答案是对这里接受的答案的改进)

请记住,在PHP中捕获处理结果提交,类似于:

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

如果有人担心 Vitaly 的观点,您可以尝试在工作 jfiddle 上进行一些裁剪和调整大小。

5赞 Gabriel Archanjo 5/29/2018 #7

在将图像上传到服务器之前,您可以使用 javascript 图像处理框架进行客户端图像处理。

下面我使用 MarvinJ 根据以下页面中的示例创建了一个可运行的代码:“在将图像上传到服务器之前在客户端处理图像”

基本上,我使用Marvin.scale(...)方法来调整图像大小。然后,我将图像上传为 blob(使用方法 image.toBlob())。服务器应答,提供所接收图像的 URL。

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

评论

0赞 pim 1/21/2019
这看起来真是太棒了!
3赞 xarlymg89 3/12/2020 #8

根据我的经验,此示例是上传调整大小的图片的最佳解决方案:https://zocada.com/compress-resize-images-javascript-browser/

它使用 HTML5 Canvas 功能。

代码非常“简单”,如下所示:

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

此解决方案有两个缺点。

第一个与图像旋转有关,因为忽略了EXIF数据。我无法解决这个问题,在我的用例中也不是那么重要,但很高兴听到任何反馈。

第二个缺点是缺乏对 IE/Edge 的支持(虽然不是基于 Chrome 的版本),我不会花时间在上面。