调整图像大小以获得特定的最大文件大小

3
我正在寻找一个函数,能够确保目标文件大小小于给定的大小,但尽可能大,并且与图像相关。原因是为了创建vcard时,照片的最大尺寸是224KB。(Apple或vcard限制:https://support.apple.com/en-us/HT202158) 我希望有一个像这样的函数:
function (imageBase64, maxFileSize)
{
    // Check if image is smaller
    // If yes, return the old one.
    // If not, reduce image size proportional to fit in maxFileSize
    // return newImage
}

你有没有什么提示来解决它?

我更喜欢在js中客户端解决它。 但如果不可能,我也接受服务器端的php解决方案。

1个回答

6
一种可能的解决方案是更改jpeg质量设置。
我已经创建了一个图像大小估算的质量设置,而不是迭代许多质量设置。
我获取最佳质量下图像的文件大小,然后使用估算大小猜测要小于文件大小的质量设置。为了尽可能接近,我上下移动质量设置并检查文件大小几次。
如果找到了一个质量设置,它的文件大小在所需大小之下且高于阈值百分比,则使用该质量设置。
如果未找到阈值,则使用通过的最佳质量质量设置。
如果遇到难以找到质量设置,则只提供非常低的设置。
如果质量设置为零,则表示失败。
所需功能
// this function converts a image to a canvas image
function image2Canvas(image){
    var canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    canvas.ctx = canvas.getContext("2d");
    canvas.ctx.drawImage(image,0,0);
    return canvas;
}
// warning try to limit calls to this function as it can cause problems on some systems
// as they try to keep up with GC
// This function gets the file size by counting the number of Base64 characters and 
// calculating the number of bytes encoded.
function getImageFileSize(image,quality){  // image must be a canvas
    return Math.floor(image.toDataURL("image/jpeg",quality).length * (3/4));
}

function qualityForSize(image,fileSize){
    // These are approximations only
    // and are the result of using a test image and finding the file size
    // at quality setting 1 to 0.1 in 0.1 steps
    const scalingFactors = [
        5638850/5638850,
        1706816/5638850,
        1257233/5638850,
        844268/5638850,
        685253/5638850,
        531014/5638850,
        474293/5638850,
        363686/5638850,
        243578/5638850,
        121475/5638850,
        0, // this is added to catch the stuff ups.
    ]
    var size = getImageFileSize(image,1); // get file size at best quality;
    if(size <= fileSize){ // best quality is a pass
        return 1;
    }
    // using size make a guess at the quality setting
    var index = 0;
    while(size * scalingFactors[index] > fileSize){ index += 1 }
    if(index === 10){  // Could not find a quality setting 
        return 0; // this is bad and should not be used as a quality setting
    }
    var sizeUpper = size * scalingFactors[index-1];  // get estimated size at upper quality
    var sizeLower = size * scalingFactors[index];  // get estimated size at lower quality
    // estimate quality via linear interpolation
    var quality = (1-(index/10)) + ((fileSize - sizeLower) / (sizeUpper-sizeLower)) * 0.1;
    var qualityStep = 0.02; // the change in quality (this value gets smaller each try)
    var numberTrys = 3;  //  number of trys to get as close as posible to the file size
    var passThreshold = 0.90; // be within 90% of desiered file size
    var passQualities = []; // array of quality settings that are under file size
    while(numberTrys--){
         var newSize = getImageFileSize(image,quality); // get the file size for quality guess
         if(newSize <= fileSize && newSize/fileSize > passThreshold ){ // does it pass?
             return quality;  // yes return quality
         }
         if(newSize > fileSize){  // file size too big 
            quality -= qualityStep;  // try lower quality
            qualityStep /= 2;        // reduce the quality step for next try
         }else{
            passQualities.push(quality);  // save this quality incase nothing get within the pass threashold
            quality += qualityStep;  // step the quality up.
            qualityStep /= 2;        // reduce the size of the next quality step     
         }
    }
    // could not find a quality setting so get the best we did find
    if(passQualities.length > 0){ //check we did get a pass
           passQualities.sort();  // sort to get best pass quality
           return passQualities.pop(); // return best quality that passed
    }
    // still no good result so just default to next 0.1 step down
    return 1-((index+1)/10);
}

如何使用

// testImage is the image to set quality of
// the image must be loaded
var imgC = image2Canvas(testImage); // convert image to a canvas
var qualitySetting = qualityForSize(imgC,244000);   // find the image quality to be under file size 244000
// convert to data URL
var dataURL = imgC.toDataURL("image/jpeg",qualitySetting);
// the saved file will be under 244000

你的解决方案很好。有一个疑问,在IE中获取的图像是png格式,尽管代码中指定了“image/jpeg”。你有任何想法为什么会发生这种情况吗? - 2k.silvio

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