2017-05-16 5 views
3

Ich bin auf der Suche nach einer Funktion, die ein Bild nimmt und dafür sorgt, dass die Zieldateigröße kleiner als die angegebene Größe ist, aber so groß wie möglich.Bildgröße ändern, um eine bestimmte max. Dateigröße

Der Grund ist für das Erstellen einer vcard die max. Die Größe des Fotos beträgt 224KB.
(Apple oder V-Card Einschränkung: https://support.apple.com/en-us/HT202158)

Ich möchte eine Funktion wie diese haben:

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 
} 

Haben Sie Hinweise, wie es zu lösen?

Ich würde diese Client-Seite in js bevorzugen. Aber wenn es nicht möglich wäre, akzeptiere ich auch serverseitige php-Lösungen.

Antwort

3

Eine mögliche Lösung ist die Änderung der JPEG-Qualitätseinstellung.

Anstatt viele Qualitätseinstellungen zu iterieren, habe ich eine Schätzung der Bildgrößen zu Qualitätseinstellungen erstellt.

Ich bekomme die Dateigröße des Bildes mit der besten Qualität dann verwenden Sie die Schätzungen Größen, um die Qualitätseinstellung zu erraten, um unter der Dateigröße zu sein. Um so nah wie möglich zu kommen, verschiebe ich die Qualitätseinstellungen nach oben und unten und überprüfe die Dateigröße einige Male.

Es gibt eine Schwellenwertgröße. Wenn eine Qualitätseinstellung gefunden wird, deren Dateigröße unter der erforderlichen Größe und über dem Schwellenwert der erforderlichen Größe liegt, wird diese Qualitätseinstellung verwendet.

Wenn der Schwellenwert nicht gefunden wird, verwendet er die beste Qualität der Qualitätseinstellung, die übergeben wurde.

Wenn es Probleme bei der Suche nach einer Qualitätseinstellung gab, wird nur eine sehr niedrige Einstellung angezeigt.

Wenn die Qualitätseinstellung Null ist, ist sie fehlgeschlagen.

Funktionen benötigt

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

Wie

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