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


102

मैंने इस सवाल का जवाब देते हुए कुछ अलग-अलग पोस्ट और यहां तक ​​कि स्टैकओवरफ्लो पर सवाल किए हैं। मैं मूल रूप से इस पोस्ट के रूप में इसी बात को लागू कर रहा हूं ।

तो यहाँ मेरा मुद्दा है। जब मैं फोटो अपलोड करता हूं, तो मुझे बाकी फॉर्म भी जमा करने होते हैं। यहाँ मेरा html है:

<form id="uploadImageForm" enctype="multipart/form-data">
  <input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
  <input id="name" value="#{name}" />
  ... a few more inputs ... 
</form>

पहले, मुझे छवि का आकार बदलने की आवश्यकता नहीं थी, इसलिए मेरी जावास्क्रिप्ट इस तरह दिखती थी:

window.uploadPhotos = function(url){
    var data = new FormData($("form[id*='uploadImageForm']")[0]);

    $.ajax({
        url: url,
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        type: 'POST',
        success: function(data){
            ... handle error...
            }
        }
    });
};

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

window.uploadPhotos = function(url){

    var resizedImage;

    // Read in file
    var file = event.target.files[0];

    // Ensure it's an image
    if(file.type.match(/image.*/)) {
        console.log('An image has been loaded');

        // Load the image
        var reader = new FileReader();
        reader.onload = function (readerEvent) {
            var image = new Image();
            image.onload = function (imageEvent) {

                // Resize the image
                var canvas = document.createElement('canvas'),
                    max_size = 1200,
                    width = image.width,
                    height = image.height;
                if (width > height) {
                    if (width > max_size) {
                        height *= max_size / width;
                        width = max_size;
                    }
                } else {
                    if (height > max_size) {
                        width *= max_size / height;
                        height = max_size;
                    }
                }
                canvas.width = width;
                canvas.height = height;
                canvas.getContext('2d').drawImage(image, 0, 0, width, height);
                resizedImage = canvas.toDataURL('image/jpeg');
            }
            image.src = readerEvent.target.result;
        }
        reader.readAsDataURL(file);
    }


   // TODO: Need some logic here to switch out which photo is being posted...

    var data = new FormData($("form[id*='uploadImageForm']")[0]);

    $.ajax({
        url: url,
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        type: 'POST',
        success: function(data){
            ... handle error...
            }
        }
    });
};

मैंने फ़ाइल इनपुट को फ़ॉर्म से बाहर ले जाने और फॉर्म में छिपे इनपुट के बारे में सोचा है कि मैंने आकार की छवि के मूल्य को निर्धारित किया है ... लेकिन मैं सोच रहा हूँ कि क्या मैं बस उस छवि को बदल सकता हूँ पहले से ही फॉर्म में है।


क्या आप किसी भी सर्वर साइड भाषा या केवल html5 और जावास्क्रिप्ट के साथ काम कर रहे हैं?
luke2012

1
@ luke2012 जावा सर्वर साइड
ferics2

हो सकता है कि jCrop जैसी किसी चीज़ का उपयोग करके क्लाइंट की तरफ छवि को क्रॉप करें और फिर सर्वर साइड में निर्देशांक भेजें और इसे क्रॉप करें। यानीBufferedImage dest = src.getSubimage(rect.x, rect.y, rect.width, rect.height);
luke2012

4
@ luke2012 बिंदु यह है कि छवि का आकार कम करके इसे सर्वर पर भेजा जाए।
ferics2

Pandamatak.com/people/anand/test/crop के js स्रोत पर एक नज़र डालें .. ऐसा लगता है ..
luke2012

जवाबों:


161

यहाँ मैं कर रहा हूँ और यह महान काम किया।

पहले मैंने फ़ाइल इनपुट को फ़ॉर्म के बाहर स्थानांतरित किया ताकि इसे सबमिट न किया जाए:

<input name="imagefile[]" type="file" id="takePictureField" accept="image/*" onchange="uploadPhotos(\'#{imageUploadUrl}\')" />
<form id="uploadImageForm" enctype="multipart/form-data">
    <input id="name" value="#{name}" />
    ... a few more inputs ... 
</form>

फिर मैंने uploadPhotosकेवल आकार बदलने के लिए फ़ंक्शन को बदल दिया :

window.uploadPhotos = function(url){
    // Read in file
    var file = event.target.files[0];

    // Ensure it's an image
    if(file.type.match(/image.*/)) {
        console.log('An image has been loaded');

        // Load the image
        var reader = new FileReader();
        reader.onload = function (readerEvent) {
            var image = new Image();
            image.onload = function (imageEvent) {

                // Resize the image
                var canvas = document.createElement('canvas'),
                    max_size = 544,// TODO : pull max size from a site config
                    width = image.width,
                    height = image.height;
                if (width > height) {
                    if (width > max_size) {
                        height *= max_size / width;
                        width = max_size;
                    }
                } else {
                    if (height > max_size) {
                        width *= max_size / height;
                        height = max_size;
                    }
                }
                canvas.width = width;
                canvas.height = height;
                canvas.getContext('2d').drawImage(image, 0, 0, width, height);
                var dataUrl = canvas.toDataURL('image/jpeg');
                var resizedImage = dataURLToBlob(dataUrl);
                $.event.trigger({
                    type: "imageResized",
                    blob: resizedImage,
                    url: dataUrl
                });
            }
            image.src = readerEvent.target.result;
        }
        reader.readAsDataURL(file);
    }
};

जैसा कि आप देख सकते हैं कि मैं canvas.toDataURL('image/jpeg');आकार की छवि को एक डेटा यूआरएल एडन में बदलने के लिए उपयोग कर रहा हूं तो मैं फ़ंक्शन dataURLToBlob(dataUrl);को कॉल करने के लिए डेटायूएलआर को एक बूँद में बदल देता हूं जिसे मैं फिर फॉर्म में जोड़ सकता हूं। जब ब्लॉब बनाया जाता है, तो मैं एक कस्टम इवेंट ट्रिगर करता हूं। यहाँ बूँद बनाने के लिए कार्य है:

/* Utility function to convert a canvas to a BLOB */
var dataURLToBlob = function(dataURL) {
    var BASE64_MARKER = ';base64,';
    if (dataURL.indexOf(BASE64_MARKER) == -1) {
        var parts = dataURL.split(',');
        var contentType = parts[0].split(':')[1];
        var raw = parts[1];

        return new Blob([raw], {type: contentType});
    }

    var parts = dataURL.split(BASE64_MARKER);
    var contentType = parts[0].split(':')[1];
    var raw = window.atob(parts[1]);
    var rawLength = raw.length;

    var uInt8Array = new Uint8Array(rawLength);

    for (var i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
    }

    return new Blob([uInt8Array], {type: contentType});
}
/* End Utility function to convert a canvas to a BLOB      */

अंत में, यहां मेरा ईवेंट हैंडलर है जो कस्टम इवेंट से बूँद लेता है, फॉर्म को जोड़ता है और फिर उसे सबमिट करता है।

/* Handle image resized events */
$(document).on("imageResized", function (event) {
    var data = new FormData($("form[id*='uploadImageForm']")[0]);
    if (event.blob && event.url) {
        data.append('image_data', event.blob);

        $.ajax({
            url: event.url,
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',
            success: function(data){
               //handle errors...
            }
        });
    }
});

1
अद्भुत कोड, कुछ त्रुटियों को ठीक करने और ठीक करने के बाद (मुझे ठीक से याद नहीं है कि मुझे क्या और कौन सा काम मिला है)। वैसे, मुझे लगता है कि जब आपने लिखा था width *= max_size / width;तो आप वास्तव में थे width *= max_size / height;
user1111929

2
क्या यह कोड मोबाइल उपकरणों पर काम करता है? IOS और Android के तहत?
प्लानवॉकर

4
@planewalker I ने वास्तव में विशेष रूप से मोबाइल उपकरणों के लिए कोड लिखा था। डेटा उपयोग में कटौती करने के लिए।
ferics2

2
@planewalker, जब मैंने यह लिखा था तो मैं iOS पर परीक्षण कर रहा था। आशा है कि यह आप के लिए काम करता है।
फेरिक्स 2

3
अच्छी तरह से किया और साझा करने के लिए धन्यवाद! (अन्य त्रुटि का उल्लेख किया है, लेकिन पहचान नहीं की गई छवि ट्रिगर ट्रिगर की छवि में है। "url: url" को "url: dataUrl" होना चाहिए)
सोरस

44

यदि कोई दिलचस्पी है तो मैंने एक टाइपस्क्रिप्ट संस्करण बनाया है:

interface IResizeImageOptions {
  maxSize: number;
  file: File;
}
const resizeImage = (settings: IResizeImageOptions) => {
  const file = settings.file;
  const maxSize = settings.maxSize;
  const reader = new FileReader();
  const image = new Image();
  const canvas = document.createElement('canvas');
  const dataURItoBlob = (dataURI: string) => {
    const bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
        atob(dataURI.split(',')[1]) :
        unescape(dataURI.split(',')[1]);
    const mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
    const max = bytes.length;
    const ia = new Uint8Array(max);
    for (var i = 0; i < max; i++) ia[i] = bytes.charCodeAt(i);
    return new Blob([ia], {type:mime});
  };
  const resize = () => {
    let width = image.width;
    let height = image.height;

    if (width > height) {
        if (width > maxSize) {
            height *= maxSize / width;
            width = maxSize;
        }
    } else {
        if (height > maxSize) {
            width *= maxSize / height;
            height = maxSize;
        }
    }

    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(image, 0, 0, width, height);
    let dataUrl = canvas.toDataURL('image/jpeg');
    return dataURItoBlob(dataUrl);
  };

  return new Promise((ok, no) => {
      if (!file.type.match(/image.*/)) {
        no(new Error("Not an image"));
        return;
      }

      reader.onload = (readerEvent: any) => {
        image.onload = () => ok(resize());
        image.src = readerEvent.target.result;
      };
      reader.readAsDataURL(file);
  })    
};

और यहाँ जावास्क्रिप्ट परिणाम है:

var resizeImage = function (settings) {
    var file = settings.file;
    var maxSize = settings.maxSize;
    var reader = new FileReader();
    var image = new Image();
    var canvas = document.createElement('canvas');
    var dataURItoBlob = function (dataURI) {
        var bytes = dataURI.split(',')[0].indexOf('base64') >= 0 ?
            atob(dataURI.split(',')[1]) :
            unescape(dataURI.split(',')[1]);
        var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
        var max = bytes.length;
        var ia = new Uint8Array(max);
        for (var i = 0; i < max; i++)
            ia[i] = bytes.charCodeAt(i);
        return new Blob([ia], { type: mime });
    };
    var resize = function () {
        var width = image.width;
        var height = image.height;
        if (width > height) {
            if (width > maxSize) {
                height *= maxSize / width;
                width = maxSize;
            }
        } else {
            if (height > maxSize) {
                width *= maxSize / height;
                height = maxSize;
            }
        }
        canvas.width = width;
        canvas.height = height;
        canvas.getContext('2d').drawImage(image, 0, 0, width, height);
        var dataUrl = canvas.toDataURL('image/jpeg');
        return dataURItoBlob(dataUrl);
    };
    return new Promise(function (ok, no) {
        if (!file.type.match(/image.*/)) {
            no(new Error("Not an image"));
            return;
        }
        reader.onload = function (readerEvent) {
            image.onload = function () { return ok(resize()); };
            image.src = readerEvent.target.result;
        };
        reader.readAsDataURL(file);
    });
};

उपयोग की तरह है:

resizeImage({
    file: $image.files[0],
    maxSize: 500
}).then(function (resizedImage) {
    console.log("upload resized image")
}).catch(function (err) {
    console.error(err);
});

या ( async/ await):

const config = {
    file: $image.files[0],
    maxSize: 500
};
const resizedImage = await resizeImage(config)
console.log("upload resized image")

वास्तव में अच्छा समाधान, आधार के रूप में परिणाम को वापस करने की अनुमति देने के लिए एक अतिरिक्त सेटिंग जोड़ सकता है (अंत में dataURIToBlob कॉल को बायपास करें)।
चेन

मुझे यह त्रुटि मिलती है:Uncaught (in promise) TypeError: Cannot read property 'type' of undefined
ओवेन एम

1
अच्छा लगा। मुझे अनसैप्टर को (<किसी भी> विंडो) में बदलना पड़ा। ट्यूनस्केप मैंने मिनसाइज़ को भी जोड़ा।
रॉबर्ट राजा

maxSizeबाइट्स या kb में है?
जागृति

1
आपके उत्तर ने मुझे इस प्रश्न का उत्तर देने में मदद की , धन्यवाद।
सैयद

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