2013-02-03 32 views
45

टी एल; डॉ;कैसे ब्राउज़र में Javascript के माध्यम से एक छवि को संपीड़ित करने के?

क्या इसे अपलोड करने से पहले एक छवि (ज्यादातर जेपीईजी, पीएनजी और जीआईएफ) सीधे ब्राउज़र-पक्ष को संपीड़ित करने का कोई तरीका है? मुझे यकीन है कि जावास्क्रिप्ट यह कर सकता है, लेकिन मुझे इसे प्राप्त करने का कोई तरीका नहीं मिल रहा है।


यहाँ पूर्ण परिदृश्य मैं लागू करने के लिए चाहते हैं क्या है:,

  • उपयोगकर्ता अपनी वेबसाइट पर चला जाता है, और एक input type="file" तत्व के माध्यम से एक छवि का चयन
  • इस छवि जावास्क्रिप्ट के माध्यम से लिया गया है, हम करते हैं कुछ सत्यापन जैसे कि सही फ़ाइल प्रारूप, अधिकतम फ़ाइल आकार इत्यादि,
  • यदि हर चीज ठीक है, तो छवि का पूर्वावलोकन पृष्ठ पर प्रदर्शित होता है,
  • उपयोगकर्ता कुछ बुनियादी संचालन कर सकता है जैसे उपयोगकर्ता के रूप में द्वारा 90 °/-90 ° छवि को घुमाने, फसल यह एक पूर्व निर्धारित अनुपात निम्नलिखित, आदि, या उपयोगकर्ता एक और छवि अपलोड करें और लौट सकते हैं 1 कदम,
  • उपयोगकर्ता संतुष्ट है जब, संपादित छवि तो है संपीड़ित और "सहेजा गया" स्थानीय रूप से (फ़ाइल में सहेजा नहीं गया है, लेकिन ब्राउज़र मेमोरी/पेज में), -
  • उपयोगकर्ता नाम, आयु आदि,
  • जैसे डेटा के साथ एक फॉर्म भरें, उपयोगकर्ता "फिनिश" पर क्लिक करें बटन, तो प्रपत्र datas + संकुचित छवि युक्त (AJAX) के बिना सर्वर के लिए भेजा जाता है,

अंतिम चरण के लिए पूर्ण प्रक्रिया में तेजी ग्राहक के पक्ष से किया जाना चाहिए, और नवीनतम Chrome और Firefox पर संगत होना चाहिए , सफारी 5 + और आईई 8। यदि संभव हो, तो केवल जावास्क्रिप्ट का उपयोग किया जाना चाहिए (लेकिन मुझे पूरा यकीन है कि यह संभव नहीं है)।

मैं कुछ भी कोड गए नहीं अभी, लेकिन मैं इसके बारे में पहले से ही सोचा है। फ़ाइल स्थानीय रूप से पढ़ने File API के माध्यम से संभव है, छवि पूर्वावलोकन और संपादन Canvas तत्व का उपयोग किया जा सकता है, लेकिन मैं छवि संपीड़न हिस्सा करने के लिए एक रास्ता नहीं मिल रहा।

html5please.com और caniuse.com के अनुसार, उन ब्राउज़र का समर्थन काफी कठिन (आईई करने के लिए धन्यवाद) है, लेकिन FlashCanvas और FileReader के रूप में इस तरह के polyfill का उपयोग किया जा सकता है।

दरअसल, लक्ष्य फ़ाइल आकार को कम करना है, इसलिए मुझे समाधान के रूप में छवि संपीड़न दिखाई देता है। लेकिन, मुझे पता है कि अपलोड की गई छवियों को मेरी वेबसाइट पर हर बार एक ही स्थान पर प्रदर्शित किया जा रहा है, और मुझे इस प्रदर्शन क्षेत्र (जैसे 200x400) का आयाम पता है। इसलिए, मैं उन आयामों को फिट करने के लिए छवि का आकार बदल सकता हूं, इस प्रकार फ़ाइल आकार को कम कर सकता हूं। मुझे नहीं पता कि इस तकनीक के लिए संपीड़न अनुपात क्या होगा।

आपको क्या लगता है? क्या आपको मुझे बताने की कोई सलाह है? क्या आप जावास्क्रिप्ट में एक छवि ब्राउज़र-पक्ष को संपीड़ित करने के किसी भी तरीके से जानते हैं? आपके उत्तरों के लिए धन्यवाद।

+0

"केवल जावास्क्रिप्ट इस्तेमाल किया जाना चाहिए ** (लेकिन मैं यकीन है कि यह संभव नहीं है हूँ) ** ** "शायद आप 1 9 80 की तरह प्रोग्रामिंग को रोक सकते हैं जहां यह संभव नहीं है, और 2017 की तरह प्रोग्रामिंग शुरू करना जहां यह संभव है। :) –

उत्तर

85

संक्षेप में:

  • साथ एचटीएमएल 5 FileReader एपीआई का उपयोग कर फ़ाइलें पढ़ें।readAsArrayBuffer
  • फ़ाइल डेटा के साथ ई ब्लॉब बनाएँ और window.URL.createObjectURL(blob)
  • के साथ अपने यूआरएल पाने नई छवि तत्व बनाएँ और सेट यह फ़ाइल ब्लॉब यूआरएल
  • को src है कैनवास पर चित्र भेजें। कैनवास आकार वांछित निर्गम आकार के लिए सेट है
  • बढ़ाया डाउन डेटा canvas.toDataURL ("image/jpeg", 0.7) (अपने स्वयं के उत्पादन प्रारूप और गुणवत्ता सेट)
  • नई छिपा आदानों संलग्न के माध्यम से कैनवास से वापस जाओ मूल रूप के और, मूल रूप से dataURI छवियों हस्तांतरण सामान्य पाठ
  • के रूप में बैकएंड पर dataURI, Base64 से डिकोड पढ़ा है, और इसे बचा

स्रोत: code

+0

बहुत बहुत धन्यवाद! यह वही है जिसे मैं देख रहा था। क्या आप जानते हैं कि इस तकनीक के साथ संपीड़न अनुपात कितना अच्छा है? – pomeh

+0

नेटवर्क ट्रांसमिशन के अलावा (आप बेस 64 एन्कोडेड सामग्री भेज रहे हैं, जो कि सबसे अच्छा नहीं है), छवि संपीड़न अल्घोरिदम मानक में से एक है, आकार आपके द्वारा चुने गए गुणवत्ता और प्रारूप पर निर्भर करता है। – psychowood

+0

सटीक रूप से सही। –

3

जहाँ तक मुझे पता है, आप कैनवास का उपयोग कर छवियों को संपीड़ित नहीं कर सकते हैं, इसके बजाय, आप इसका आकार बदल सकते हैं। Canvas.toDataURL का उपयोग करने से आप संपीड़न अनुपात का उपयोग करने के लिए चुनने नहीं देंगे। आप कैनिमेज पर एक नज़र डाल सकते हैं जो आप चाहते हैं: https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js

असल में, छवि के आकार को कम करने के लिए छवि का आकार बदलने के लिए अक्सर पर्याप्त होता है लेकिन यदि आप आगे जाना चाहते हैं, तो आपको नए परिचय का उपयोग करना होगा छवि डेटा युक्त एक बफर प्राप्त करने के लिए विधि file.readAsArrayBuffer।

फिर, छवि प्रारूप विनिर्देश (http://en.wikipedia.org/wiki/JPEG या http://en.wikipedia.org/wiki/Portable_Network_Graphics) के अनुसार इसकी सामग्री को पढ़ने के लिए बस डेटाव्यू का उपयोग करें।

छवि डेटा संपीड़न से निपटना मुश्किल होगा, लेकिन यह एक कोशिश है। दूसरी तरफ, आप अपनी छवि को छोटा बनाने के लिए पीएनजी हेडर या जेपीईजी एक्सिफ डेटा को हटाने का प्रयास कर सकते हैं, ऐसा करना आसान होना चाहिए।

आपको एक और बफर पर एक और डेटाव्यू बनाना होगा और इसे फ़िल्टर की गई छवि सामग्री से भरना होगा। फिर, आपको window.btoa का उपयोग करके डेटायूआरआई पर छवि सामग्री को एन्कोड करना होगा।

अगर आप कुछ ऐसा लागू करते हैं तो मुझे बताएं, कोड के माध्यम से जाना दिलचस्प होगा।

8

@PsychoWoods का जवाब अच्छा है। मैं अपना खुद का समाधान देना चाहता हूं। यह जावास्क्रिप्ट फ़ंक्शन एक छवि डेटा यूआरएल और चौड़ाई लेता है, इसे नई चौड़ाई तक स्केल करता है, और एक नया डेटा यूआरएल देता है।

// Take an image URL, downscale it to the given width, and return a new image URL. 
function downscaleImage(dataUrl, newWidth, imageType, imageArguments) { 
    "use strict"; 
    var image, oldWidth, oldHeight, newHeight, canvas, ctx, newDataUrl; 

    // Provide default values 
    imageType = imageType || "image/jpeg"; 
    imageArguments = imageArguments || 0.7; 

    // Create a temporary image so that we can compute the height of the downscaled image. 
    image = new Image(); 
    image.src = dataUrl; 
    oldWidth = image.width; 
    oldHeight = image.height; 
    newHeight = Math.floor(oldHeight/oldWidth * newWidth) 

    // Create a temporary canvas to draw the downscaled image on. 
    canvas = document.createElement("canvas"); 
    canvas.width = newWidth; 
    canvas.height = newHeight; 

    // Draw the downscaled image on the canvas and return the new data URL. 
    ctx = canvas.getContext("2d"); 
    ctx.drawImage(image, 0, 0, newWidth, newHeight); 
    newDataUrl = canvas.toDataURL(imageType, imageArguments); 
    return newDataUrl; 
} 

इस कोड को कहीं भी इस्तेमाल किया जा सकता आप एक डेटा URL है और एक downscaled छवि के लिए एक डेटा यूआरएल चाहते हैं।

+0

कृपया, क्या आप मुझे इस उदाहरण के बारे में अधिक जानकारी दे सकते हैं, फ़ंक्शन को कैसे कॉल करें और परिणाम कैसे लौटाया? – nabil

+0

यहां एक उदाहरण दिया गया है: http://danielsadventure.info/Html/scaleimage.html यह देखने के लिए पृष्ठ के स्रोत को पढ़ना सुनिश्चित करें कि यह कैसे काम करता है। –

0

आप सबसे अच्छा संपीड़न JIC (जावास्क्रिप्ट छवि संपीड़न) नामक तकनीक का उपयोग कर सकते हैं यह निश्चित रूप से मदद मिलेगी जेपीजी छवि संपीड़न के लिए ->https://github.com/brunobar79/J-I-C

+1

गुणवत्ता को कम किए बिना –

5

मैं दो बातें अन्य उत्तर से लापता देखें:

  • canvas.toBlob (जब उपलब्ध हो) , और async से अधिक प्रदर्शन करने वाला है।
  • फ़ाइल -> छवि -> कैनवास -> फ़ाइल रूपांतरण EXIF ​​डेटा खो देता है; विशेष रूप से, छवि रोटेशन के बारे में डेटा आमतौर पर आधुनिक फोन/टैबलेट द्वारा निर्धारित किया जाता है।दोनों अंकों के साथ

निम्न स्क्रिप्ट सौदों:

// From https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob, needed for Safari: 
if (!HTMLCanvasElement.prototype.toBlob) { 
    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', { 
     value: function(callback, type, quality) { 

      var binStr = atob(this.toDataURL(type, quality).split(',')[1]), 
       len = binStr.length, 
       arr = new Uint8Array(len); 

      for (var i = 0; i < len; i++) { 
       arr[i] = binStr.charCodeAt(i); 
      } 

      callback(new Blob([arr], {type: type || 'image/png'})); 
     } 
    }); 
} 

window.URL = window.URL || window.webkitURL; 

// Modified from https://stackoverflow.com/a/32490603, cc by-sa 3.0 
// -2 = not jpeg, -1 = no data, 1..8 = orientations 
function getExifOrientation(file, callback) { 
    // Suggestion from http://code.flickr.net/2012/06/01/parsing-exif-client-side-using-javascript-2/: 
    if (file.slice) { 
     file = file.slice(0, 131072); 
    } else if (file.webkitSlice) { 
     file = file.webkitSlice(0, 131072); 
    } 

    var reader = new FileReader(); 
    reader.onload = function(e) { 
     var view = new DataView(e.target.result); 
     if (view.getUint16(0, false) != 0xFFD8) { 
      callback(-2); 
      return; 
     } 
     var length = view.byteLength, offset = 2; 
     while (offset < length) { 
      var marker = view.getUint16(offset, false); 
      offset += 2; 
      if (marker == 0xFFE1) { 
       if (view.getUint32(offset += 2, false) != 0x45786966) { 
        callback(-1); 
        return; 
       } 
       var little = view.getUint16(offset += 6, false) == 0x4949; 
       offset += view.getUint32(offset + 4, little); 
       var tags = view.getUint16(offset, little); 
       offset += 2; 
       for (var i = 0; i < tags; i++) 
        if (view.getUint16(offset + (i * 12), little) == 0x0112) { 
         callback(view.getUint16(offset + (i * 12) + 8, little)); 
         return; 
        } 
      } 
      else if ((marker & 0xFF00) != 0xFF00) break; 
      else offset += view.getUint16(offset, false); 
     } 
     callback(-1); 
    }; 
    reader.readAsArrayBuffer(file); 
} 

// Derived from https://stackoverflow.com/a/40867559, cc by-sa 
function imgToCanvasWithOrientation(img, rawWidth, rawHeight, orientation) { 
    var canvas = document.createElement('canvas'); 
    if (orientation > 4) { 
     canvas.width = rawHeight; 
     canvas.height = rawWidth; 
    } else { 
     canvas.width = rawWidth; 
     canvas.height = rawHeight; 
    } 

    if (orientation > 1) { 
     console.log("EXIF orientation = " + orientation + ", rotating picture"); 
    } 

    var ctx = canvas.getContext('2d'); 
    switch (orientation) { 
     case 2: ctx.transform(-1, 0, 0, 1, rawWidth, 0); break; 
     case 3: ctx.transform(-1, 0, 0, -1, rawWidth, rawHeight); break; 
     case 4: ctx.transform(1, 0, 0, -1, 0, rawHeight); break; 
     case 5: ctx.transform(0, 1, 1, 0, 0, 0); break; 
     case 6: ctx.transform(0, 1, -1, 0, rawHeight, 0); break; 
     case 7: ctx.transform(0, -1, -1, 0, rawHeight, rawWidth); break; 
     case 8: ctx.transform(0, -1, 1, 0, 0, rawWidth); break; 
    } 
    ctx.drawImage(img, 0, 0, rawWidth, rawHeight); 
    return canvas; 
} 

function reduceFileSize(file, acceptFileSize, maxWidth, maxHeight, quality, callback) { 
    if (file.size <= acceptFileSize) { 
     callback(file); 
     return; 
    } 
    var img = new Image(); 
    img.onerror = function() { 
     URL.revokeObjectURL(this.src); 
     callback(file); 
    }; 
    img.onload = function() { 
     URL.revokeObjectURL(this.src); 
     getExifOrientation(file, function(orientation) { 
      var w = img.width, h = img.height; 
      var scale = (orientation > 4 ? 
       Math.min(maxHeight/w, maxWidth/h, 1) : 
       Math.min(maxWidth/w, maxHeight/h, 1)); 
      h = Math.round(h * scale); 
      w = Math.round(w * scale); 

      var canvas = imgToCanvasWithOrientation(img, w, h, orientation); 
      canvas.toBlob(function(blob) { 
       console.log("Resized image to " + w + "x" + h + ", " + (blob.size >> 10) + "kB"); 
       callback(blob); 
      }, 'image/jpeg', quality); 
     }); 
    }; 
    img.src = URL.createObjectURL(file); 
} 

उदाहरण उपयोग:

inputfile.onchange = function() { 
    // If file size > 500kB, resize such that width <= 1000, quality = 0.9 
    reduceFileSize(this.files[0], 500*1024, 1000, Infinity, 0.9, blob => { 
     let body = new FormData(); 
     body.set('file', blob, blob.name || "file.jpg"); 
     fetch('/upload-image', {method: 'POST', body}).then(...); 
    }); 
}; 
+0

ToBlob ने मेरे लिए चाल बनाई, फ़ाइल बनाकर सर्वर पर $ _FILES सरणी में प्राप्त किया। धन्यवाद! –