HTML5 अपलोड करने से पहले छवियों का आकार बदलें


181

यहाँ एक नूडल स्क्रैचर है।

ध्यान में रखते हुए हम HTML5 स्थानीय भंडारण और xhr v2 और क्या नहीं है। मैं सोच रहा था कि क्या किसी को भी एक काम करने का उदाहरण मिल सकता है या यहाँ तक कि मुझे इस प्रश्न के लिए हां या नहीं भी देना चाहिए:

क्या नए स्थानीय भंडारण (या जो भी हो) का उपयोग करके एक छवि को पूर्व-आकार देना संभव है, ताकि एक उपयोगकर्ता जो छवि को आकार देने के बारे में कोई सुराग नहीं लगाता है, वह अपनी 10mb छवि को मेरी वेबसाइट में खींच सकता है, यह नए स्थानीय स्टोर का उपयोग करके इसे आकार देता है; फिर इसे छोटे आकार में अपलोड करें।

मैं अच्छी तरह से जानता हूं कि आप इसे फ्लैश, जावा एप्लेट्स, सक्रिय एक्स के साथ कर सकते हैं ... सवाल यह है कि क्या आप जावास्क्रिप्ट + एचटीएमएल 5 के साथ कर सकते हैं।

इस पर प्रतिक्रिया की प्रतीक्षा कर रहा है।

अभी के लिए टा।

जवाबों:


181

हां, फ़ाइल एपीआई का उपयोग करें , फिर आप छवियों को कैनवास तत्व के साथ संसाधित कर सकते हैं ।

यह मोज़िला हैक्स ब्लॉग पोस्ट आपको अधिकांश प्रक्रिया के माध्यम से चलता है। यहाँ संदर्भ के लिए ब्लॉग पोस्ट से इकट्ठे स्रोत कोड है:

// from an input element
var filesToUpload = input.files;
var file = filesToUpload[0];

var img = document.createElement("img");
var reader = new FileReader();  
reader.onload = function(e) {img.src = e.target.result}
reader.readAsDataURL(file);

var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);

var MAX_WIDTH = 800;
var MAX_HEIGHT = 600;
var width = img.width;
var height = img.height;

if (width > height) {
  if (width > MAX_WIDTH) {
    height *= MAX_WIDTH / width;
    width = MAX_WIDTH;
  }
} else {
  if (height > MAX_HEIGHT) {
    width *= MAX_HEIGHT / height;
    height = MAX_HEIGHT;
  }
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);

var dataurl = canvas.toDataURL("image/png");

//Post dataurl to the server with AJAX

6
क्यों धन्यवाद सर। मैं आज रात एक नाटक होगा ... फ़ाइल एपीआई के साथ कि है। मुझे काम करने के लिए ड्रैग और ड्रॉप अपलोड मिला और मैंने महसूस किया कि यह भी शामिल करने के लिए एक बहुत अच्छी सुविधा होगी। याहू।
जिममीत १

3
फ़ाइल आकार को कम करने के लिए गुणवत्ता लाने के लिए सुनिश्चित करने के लिए हमें इस mycanvas.toDataURL ("image / jpeg", 0.5) का उपयोग करने की आवश्यकता नहीं है। मैं इस टिप्पणी पर मोज़िला ब्लॉग पोस्ट पर आया और यहाँ बग रिज़ॉल्यूशन देखा Bugzilla.mozilla.org/show_bug.cgi?id=564388
sbose

33
आपको onloadछवि पर इंतजार करना चाहिए ( सेटिंग से पहले हैंडलर संलग्न करें src), क्योंकि ब्राउज़र को एसिंक्रोनस रूप से डिकोडिंग करने की अनुमति है और पिक्सेल पहले उपलब्ध नहीं हो सकते हैं drawImage
कोर्नेल

6
आप कैनवास कोड को फाइलर के ऑनलोड फ़ंक्शन में डालना चाह सकते हैं - एक मौका है कि ctx.drawImage()अंत से पहले आपके पास बड़ी छवियां लोड नहीं होंगी ।
ग्रेग

4
सावधान
रहें

107

मैंने कुछ साल पहले इस समस्या से निपटा और मेरे समाधान को https://github.com/rossturner/HTML5-ImageUploader के रूप में अपलोड किया

robertc का जवाब मोज़िला हैक्स ब्लॉग पोस्ट में प्रस्तावित समाधान का उपयोग करता है , हालांकि मैंने पाया कि यह वास्तव में खराब छवि गुणवत्ता का था जब 2: 1 (या एक से अधिक) का आकार नहीं था। मैंने एल्गोरिदम का आकार बदलने वाली अलग-अलग छवि के साथ प्रयोग करना शुरू कर दिया, हालांकि अधिकांश का अंत काफी धीमा था या फिर गुणवत्ता में महान नहीं थे।

अंत में मैं एक समाधान के साथ आया था जो मुझे विश्वास है कि जल्दी से निष्पादित होता है और इसमें बहुत अच्छा प्रदर्शन भी है - जैसा कि 1 कैनवस से दूसरे कैनवास पर कॉपी करने का मोज़िला समाधान 2: 1 के अनुपात में छवि गुणवत्ता के नुकसान के बिना जल्दी से काम करता है, जिसे x का लक्ष्य दिया गया है। पिक्सेल चौड़े और y पिक्सेल लम्बे होते हैं, मैं इस कैनवास को आकार देने की विधि का उपयोग करता हूँ जब तक कि चित्र x और 2 x और y और 2 y के बीच न हो । इस बिंदु पर मैं फिर लक्ष्य आकार के आकार के अंतिम "चरण" के लिए एल्गोरिथम छवि का आकार बदल देता हूं। कई अलग-अलग एल्गोरिदम की कोशिश करने के बाद, मैं एक ब्लॉग से लिया गया बिलिनियर इंटरपोलेशन पर बस गया, जो अब ऑनलाइन नहीं है, लेकिन इंटरनेट आर्काइव के माध्यम से सुलभ है, जो अच्छे परिणाम देता है, यहां लागू कोड है:

ImageUploader.prototype.scaleImage = function(img, completionCallback) {
    var canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);

    while (canvas.width >= (2 * this.config.maxWidth)) {
        canvas = this.getHalfScaleCanvas(canvas);
    }

    if (canvas.width > this.config.maxWidth) {
        canvas = this.scaleCanvasWithAlgorithm(canvas);
    }

    var imageData = canvas.toDataURL('image/jpeg', this.config.quality);
    this.performUpload(imageData, completionCallback);
};

ImageUploader.prototype.scaleCanvasWithAlgorithm = function(canvas) {
    var scaledCanvas = document.createElement('canvas');

    var scale = this.config.maxWidth / canvas.width;

    scaledCanvas.width = canvas.width * scale;
    scaledCanvas.height = canvas.height * scale;

    var srcImgData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
    var destImgData = scaledCanvas.getContext('2d').createImageData(scaledCanvas.width, scaledCanvas.height);

    this.applyBilinearInterpolation(srcImgData, destImgData, scale);

    scaledCanvas.getContext('2d').putImageData(destImgData, 0, 0);

    return scaledCanvas;
};

ImageUploader.prototype.getHalfScaleCanvas = function(canvas) {
    var halfCanvas = document.createElement('canvas');
    halfCanvas.width = canvas.width / 2;
    halfCanvas.height = canvas.height / 2;

    halfCanvas.getContext('2d').drawImage(canvas, 0, 0, halfCanvas.width, halfCanvas.height);

    return halfCanvas;
};

ImageUploader.prototype.applyBilinearInterpolation = function(srcCanvasData, destCanvasData, scale) {
    function inner(f00, f10, f01, f11, x, y) {
        var un_x = 1.0 - x;
        var un_y = 1.0 - y;
        return (f00 * un_x * un_y + f10 * x * un_y + f01 * un_x * y + f11 * x * y);
    }
    var i, j;
    var iyv, iy0, iy1, ixv, ix0, ix1;
    var idxD, idxS00, idxS10, idxS01, idxS11;
    var dx, dy;
    var r, g, b, a;
    for (i = 0; i < destCanvasData.height; ++i) {
        iyv = i / scale;
        iy0 = Math.floor(iyv);
        // Math.ceil can go over bounds
        iy1 = (Math.ceil(iyv) > (srcCanvasData.height - 1) ? (srcCanvasData.height - 1) : Math.ceil(iyv));
        for (j = 0; j < destCanvasData.width; ++j) {
            ixv = j / scale;
            ix0 = Math.floor(ixv);
            // Math.ceil can go over bounds
            ix1 = (Math.ceil(ixv) > (srcCanvasData.width - 1) ? (srcCanvasData.width - 1) : Math.ceil(ixv));
            idxD = (j + destCanvasData.width * i) * 4;
            // matrix to vector indices
            idxS00 = (ix0 + srcCanvasData.width * iy0) * 4;
            idxS10 = (ix1 + srcCanvasData.width * iy0) * 4;
            idxS01 = (ix0 + srcCanvasData.width * iy1) * 4;
            idxS11 = (ix1 + srcCanvasData.width * iy1) * 4;
            // overall coordinates to unit square
            dx = ixv - ix0;
            dy = iyv - iy0;
            // I let the r, g, b, a on purpose for debugging
            r = inner(srcCanvasData.data[idxS00], srcCanvasData.data[idxS10], srcCanvasData.data[idxS01], srcCanvasData.data[idxS11], dx, dy);
            destCanvasData.data[idxD] = r;

            g = inner(srcCanvasData.data[idxS00 + 1], srcCanvasData.data[idxS10 + 1], srcCanvasData.data[idxS01 + 1], srcCanvasData.data[idxS11 + 1], dx, dy);
            destCanvasData.data[idxD + 1] = g;

            b = inner(srcCanvasData.data[idxS00 + 2], srcCanvasData.data[idxS10 + 2], srcCanvasData.data[idxS01 + 2], srcCanvasData.data[idxS11 + 2], dx, dy);
            destCanvasData.data[idxD + 2] = b;

            a = inner(srcCanvasData.data[idxS00 + 3], srcCanvasData.data[idxS10 + 3], srcCanvasData.data[idxS01 + 3], srcCanvasData.data[idxS11 + 3], dx, dy);
            destCanvasData.data[idxD + 3] = a;
        }
    }
};

यह config.maxWidthमूल पहलू अनुपात को बनाए रखते हुए, एक चित्र को नीचे की चौड़ाई तक फैलाता है। विकास के समय इसने प्रमुख डेस्कटॉप ब्राउज़र (IE9 +, फ़ायरफ़ॉक्स, क्रोम) के अलावा iPad / iPhone Safari पर काम किया था, इसलिए मुझे उम्मीद है कि यह आज भी HTML5 के व्यापक स्तर को देखते हुए संगत होगा। ध्यान दें कि कैनवस .toDataURL () कॉल एक माइम प्रकार और छवि गुणवत्ता लेता है जो आपको गुणवत्ता और आउटपुट फ़ाइल प्रारूप को नियंत्रित करने की अनुमति देगा (यदि आप चाहें तो इनपुट से भिन्न)।

एकमात्र बिंदु जो इसे कवर नहीं करता है, वह ओरिएंटेशन की जानकारी को बनाए रखता है, इस मेटाडेटा के ज्ञान के बिना छवि को आकार दिया जाता है और इसे बचाया जाता है, जो कि अभिविन्यास के लिए छवि के भीतर किसी भी मेटाडेटा को खो देता है, जिसका अर्थ है कि टैबलेट डिवाइस "उल्टा" पर ली गई छवियां थीं। इस तरह प्रस्तुत किया गया है, हालांकि वे डिवाइस के कैमरे के दृश्यदर्शी में फ़्लिप हो गए होंगे। यदि यह एक चिंता का विषय है, तो इस ब्लॉग पोस्ट में इसे पूरा करने के तरीके के बारे में एक अच्छा मार्गदर्शक और कोड उदाहरण हैं, जो मुझे यकीन है कि उपरोक्त कोड को एकीकृत किया जा सकता है।


4
सम्मानित किया गया कारण आपको लगता है कि मुझे लगता है कि यह उत्तर के लिए एक गुच्छा जोड़ता है, होगा कि अभिविन्यास सामान एकीकृत हो हालांकि :)
सैम भगवा

3
अभिविन्यास मेटाडाटा के लिए एक बहुत ही उपयोगी आत्मा अब कुछ कार्यक्षमता में विलीन हो गई है :)
रॉस टेलर-टर्नर

26

ऊपर सुधार:

<img src="" id="image">
<input id="input" type="file" onchange="handleFiles()">
<script>

function handleFiles()
{
    var filesToUpload = document.getElementById('input').files;
    var file = filesToUpload[0];

    // Create an image
    var img = document.createElement("img");
    // Create a file reader
    var reader = new FileReader();
    // Set the image once loaded into file reader
    reader.onload = function(e)
    {
        img.src = e.target.result;

        var canvas = document.createElement("canvas");
        //var canvas = $("<canvas>", {"id":"testing"})[0];
        var ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0);

        var MAX_WIDTH = 400;
        var MAX_HEIGHT = 300;
        var width = img.width;
        var height = img.height;

        if (width > height) {
          if (width > MAX_WIDTH) {
            height *= MAX_WIDTH / width;
            width = MAX_WIDTH;
          }
        } else {
          if (height > MAX_HEIGHT) {
            width *= MAX_HEIGHT / height;
            height = MAX_HEIGHT;
          }
        }
        canvas.width = width;
        canvas.height = height;
        var ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0, width, height);

        var dataurl = canvas.toDataURL("image/png");
        document.getElementById('image').src = dataurl;     
    }
    // Load files into file reader
    reader.readAsDataURL(file);


    // Post the data
    /*
    var fd = new FormData();
    fd.append("name", "some_filename.jpg");
    fd.append("image", dataurl);
    fd.append("info", "lah_de_dah");
    */
}</script>

2
FormData () में जोड़ने के बाद आप इसका PHP भाग कैसे संभालेंगे? आप उदाहरण के लिए $ _FILES ['नाम'] ['tmp_name'] [$ i] की तलाश नहीं करेंगे? मैं कोशिश कर रहा हूँ अगर (isset ($ _ POST ['छवि')) ... लेकिन dataurlवहाँ नहीं।
डेनिकोव

2
यह पहली बार फ़ायरफ़ॉक्स पर काम नहीं करता है जब आप एक छवि अपलोड करने का प्रयास करते हैं। अगली बार जब आप कोशिश करेंगे तो यह काम करेगा। इसे कैसे जोड़ेंगे?
फ्रेड

फ़ाइल सरणी सरणी शून्य या रिक्त है, तो वापस लौटें।
शैलप्रिया

2
Chrome में जब एक्सेस करने की कोशिश कर रहे हैं img.widthऔर img.heightशुरू में वे हैं 0। आपको शेष फ़ंक्शन को अंदर रखना चाहिएimg.addEventListener('load', function () { // now the image has loaded, continue... });
Inigo

2
@ जस्टिन क्या आप "उपरोक्त" को स्पष्ट कर सकते हैं, जैसे कि यह पोस्ट किसका सुधार है? चीयर्स
मार्टिन

16

जस्टिन द्वारा जवाब के लिए संशोधन जो मेरे लिए काम करता है:

  1. जोड़ा गया img.onload
  2. वास्तविक उदाहरण के साथ POST अनुरोध का विस्तार करें

function handleFiles()
{
    var dataurl = null;
    var filesToUpload = document.getElementById('photo').files;
    var file = filesToUpload[0];

    // Create an image
    var img = document.createElement("img");
    // Create a file reader
    var reader = new FileReader();
    // Set the image once loaded into file reader
    reader.onload = function(e)
    {
        img.src = e.target.result;

        img.onload = function () {
            var canvas = document.createElement("canvas");
            var ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0);

            var MAX_WIDTH = 800;
            var MAX_HEIGHT = 600;
            var width = img.width;
            var height = img.height;

            if (width > height) {
              if (width > MAX_WIDTH) {
                height *= MAX_WIDTH / width;
                width = MAX_WIDTH;
              }
            } else {
              if (height > MAX_HEIGHT) {
                width *= MAX_HEIGHT / height;
                height = MAX_HEIGHT;
              }
            }
            canvas.width = width;
            canvas.height = height;
            var ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, width, height);

            dataurl = canvas.toDataURL("image/jpeg");

            // Post the data
            var fd = new FormData();
            fd.append("name", "some_filename.jpg");
            fd.append("image", dataurl);
            fd.append("info", "lah_de_dah");
            $.ajax({
                url: '/ajax_photo',
                data: fd,
                cache: false,
                contentType: false,
                processData: false,
                type: 'POST',
                success: function(data){
                    $('#form_photo')[0].reset();
                    location.reload();
                }
            });
        } // img.onload
    }
    // Load files into file reader
    reader.readAsDataURL(file);
}

2
अभिविन्यास की जानकारी (
एक्जिफ़

8

यदि आप पहिया को पुनः स्थापित नहीं करना चाहते हैं तो आप plupload.com को आज़मा सकते हैं


3
मैं भी ग्राहक पक्ष के आकार बदलने के लिए plupload का उपयोग करें। इसके बारे में सबसे अच्छी बात यह है कि फ्लैश, एचटीएमएल 5, सिल्वरलाइट आदि का उपयोग कर सकते हैं, जो भी उपयोगकर्ता के पास उपलब्ध है, क्योंकि प्रत्येक उपयोगकर्ता का एक अलग सेटअप है। यदि आप केवल html5 चाहते हैं, तो मुझे लगता है कि यह कुछ पुराने ब्राउज़रों और ओपेरा पर काम नहीं करेगा।
jnl

6

टाइपप्रति

async resizeImg(file: Blob): Promise<Blob> {
    let img = document.createElement("img");
    img.src = await new Promise<any>(resolve => {
        let reader = new FileReader();
        reader.onload = (e: any) => resolve(e.target.result);
        reader.readAsDataURL(file);
    });
    await new Promise(resolve => img.onload = resolve)
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    let MAX_WIDTH = 1000;
    let MAX_HEIGHT = 1000;
    let width = img.naturalWidth;
    let height = img.naturalHeight;
    if (width > height) {
        if (width > MAX_WIDTH) {
            height *= MAX_WIDTH / width;
            width = MAX_WIDTH;
        }
    } else {
        if (height > MAX_HEIGHT) {
            width *= MAX_HEIGHT / height;
            height = MAX_HEIGHT;
        }
    }
    canvas.width = width;
    canvas.height = height;
    ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, width, height);
    let result = await new Promise<Blob>(resolve => { canvas.toBlob(resolve, 'image/jpeg', 0.95); });
    return result;
}

1
जो भी इस पद पर आ सकता है, यह वादा आधारित उत्तर मेरी राय में अधिक विश्वसनीय है। मैंने सामान्य जेएस में बदल दिया और यह एक आकर्षण की तरह काम करता है।
gbland777

5
fd.append("image", dataurl);

यह काम नहीं करेगा। PHP की तरफ आप इससे file को save नहीं कर सकते हैं।

इस कोड का उपयोग करें:

var blobBin = atob(dataurl.split(',')[1]);
var array = [];
for(var i = 0; i < blobBin.length; i++) {
  array.push(blobBin.charCodeAt(i));
}
var file = new Blob([new Uint8Array(array)], {type: 'image/png', name: "avatar.png"});

fd.append("image", file); // blob file

5

एक कैनवास तत्व में छवियों का आकार बदलना आमतौर पर बुरा विचार है क्योंकि यह सबसे सस्ता बॉक्स प्रक्षेप का उपयोग करता है। गुणवत्ता में परिणामी छवि ध्यान देने योग्य घट जाती है। मैं http://nodeca.github.io/pica/demo/ का उपयोग करने की सलाह दूंगा जो लैंक्ज़ोस परिवर्तन कर सकते हैं। ऊपर दिया गया डेमो पेज कैनवास और लैंक्ज़ोस दृष्टिकोण के बीच अंतर दिखाता है।

यह समानांतर में छवियों को आकार देने के लिए वेब श्रमिकों का भी उपयोग करता है। WEBGL कार्यान्वयन भी है।

कुछ ऑनलाइन छवि पुनर्विक्रेता हैं जो काम करने के लिए पिका का उपयोग करते हैं, जैसे https://myimageresizer.com


5

स्वीकृत उत्तर बहुत अच्छा काम करता है, लेकिन आकार बदलने वाले तर्क उस मामले को नजरअंदाज कर देते हैं जिसमें छवि केवल एक अक्ष में अधिकतम से अधिक होती है (उदाहरण के लिए, ऊंचाई> अधिकतम ऊंचाई लेकिन चौड़ाई <= मैक्सविद)।

मुझे लगता है कि निम्नलिखित कोड सभी मामलों में अधिक सीधे-आगे और कार्यात्मक तरीके से देखभाल करता है (सादे जावास्क्रिप्ट का उपयोग करते हुए टाइपस्क्रिप्ट प्रकार एनोटेशन को अनदेखा करें):

private scaleDownSize(width: number, height: number, maxWidth: number, maxHeight: number): {width: number, height: number} {
    if (width <= maxWidth && height <= maxHeight)
      return { width, height };
    else if (width / maxWidth > height / maxHeight)
      return { width: maxWidth, height: height * maxWidth / width};
    else
      return { width: width * maxHeight / height, height: maxHeight };
  }

0

आप उपयोग कर सकते हैं dropzone.js का यदि आप अपलोड कार्यों से पहले सरल और आसान अपलोड प्रबंधक का उपयोग करना चाहते हैं।

इसमें बिल्टइन रिसाइज़ फ़ंक्शंस हैं, लेकिन आप चाहें तो अपना खुद का प्रदान कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.