使用JavaScript在客户端对图像进行大小调整,然后再上传到服务器

165
我正在寻找一种使用JavaScript在客户端调整图像大小的方法(真正的缩放,而不仅仅是改变宽度和高度)。我知道可以在Flash中实现这一点,但如果可能的话,我想避免使用它。
是否有任何开源算法可以在Web上找到?

对于那些仍想知道答案的人,你可以做到这一点,但是你需要进行一些Ajax调用来进行图像处理。 - polyhedron
3
“Need to make”?那么你可以在服务器上解决它,并通过AJAX调用透明地获取数据。但整个尝试是要在客户端完成,正如Jeremy所指出的那样,这是可以做到的。我认为这是一个很好的例子:https://github.com/rossturner/HTML5-ImageUploader - Bart
2
在最新版本中,Dropzone.js支持在上传之前对客户端的图像进行调整大小。 - user1666456
8个回答

127
这是一个可以实现此功能的要点: 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>

我加入了许多支持检测和填充以确保它能在尽可能多的浏览器上正常工作。

(它也会忽略 GIF 图像 - 以防它们是动画的)


2
我觉得你没有得到足够的赞扬 - 我认为它非常好用和超级简单。谢谢分享! - Matt
4
嗯..我在这方面取得了不错的成功,但我发现我的图片(除了被调整大小之外)还遭受了巨大的质量损失(以严重的像素化形式),尽管输出时分辨率是正确的。 - Ruben Martinez Jr.
1
嗨,我需要一个像这个一样使用的代码片段,我认为这很好,但是...function(blob, didItResize) { // 如果成功调整大小,则didItResize将为true,否则为false(并将原始文件作为'blob'返回) document.getElementById('preview').src = window.URL.createObjectURL(blob); // 您现在也可以使用XHR上传此blob。在这里我有一个问题..如何通过提交表单来发送这个图像。我的意思是,像提交按钮触发的post请求那样。你知道,通常的方式。 感谢您提供的代码! - iedmrc
5
对于任何出现质量损失(重新采样)的情况,需要更新画布上下文并将imageSmoothingEnabled设置为true,并将imageSmoothingQuality设置为high。在TypeScript中,该代码块如下所示:const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height); - Sean Perkins
1
有没有可能将这个变成npm包?那就太棒了! - erksch
显示剩余5条评论

63

39
这是一个只包含链接的回答,因此应该作为一条评论发布。 - Jimbo
2
canvas.mozGetAsFile("foo.png"); 是一个被弃用的函数。 - mili

12

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

它可以用任何想象得到的方法为您自动完成所有工作。

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

我刚试了一下,它在我的安卓手机上运行良好!

我曾经使用过http://swfupload.org/中的Flash,它也很好地完成了工作,但是调整大小的尺寸非常小(无法记住限制),并且在没有Flash可用的情况下不能回到HTML4。


47
当用户试图上传一个大小为10MB的照片,但实际上只需要存储为一个小很多的照片时,进行客户端调整大小非常有用。这样上传速度会更快。 - Kyle
在这里出现了相同的情况,一旦你有一个文件输入框,用户在智能手机上使用相机拍照后,它会在现代智能手机上达到约10 MB。如果你在服务器端调整大小并且仅存储其较小版本,那么在客户端之前进行调整大小可以节省很多移动数据和加载时间。 - Alex
2
请注意,plupload 是 AGPL 许可证。 - Alex

11

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

现代浏览器可以使用画布(canvas)来加载/保存图像数据。但是,如果在客户端调整图像大小,应注意以下几点:

  1. 每个通道只有8位(相比之下,JPEG可具有更好的动态范围,约为12位)。如果您不上传专业照片,则不应该有问题。
  2. 小心调整大小算法。大多数客户端调整大小器使用简单的数学运算,结果可能比预期的还要差。
  3. 可能需要对缩小的图像进行锐化处理。
  4. 如果想重用原始文件中的元数据(如EXIF和其他),请不要忘记删除色彩配置信息。因为在将图像加载到画布时会应用它们。

6
也许可以使用canvas标签(尽管它不是可移植的)。这里有一篇关于如何使用canvas旋转图像的博客(链接),如果您能够旋转它,也许可以调整大小。也许这可以作为一个起点。
此外,可以查看这个库

2
非常有用的例子。如果这些链接失效了,您可能想在您的答案中添加一两个代码片段。 - Trevor

5
你可以使用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>


这看起来真是太棒了! - pim

3

根据我的经验,这个例子是上传调整大小图片最好的解决方案:

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的版本),我不会在这上面花费任何时间。


2
是的,使用现代浏览器完全可以做到这一点。甚至可以将文件上传为二进制文件,而不影响任何数量的画布修改。
这里有一个示例
(此答案是对此处接受的答案进行的改进)
在 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上尝试一些裁剪和调整大小的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接