วิธีบีบอัดภาพผ่าน Javascript ในเบราว์เซอร์?


93

TL; DR;

มีวิธีบีบอัดรูปภาพ (ส่วนใหญ่เป็น jpeg, png และ gif) โดยตรงทางฝั่งเบราว์เซอร์ก่อนที่จะอัปโหลดหรือไม่? ฉันค่อนข้างมั่นใจว่า JavaScript สามารถทำได้ แต่ฉันไม่สามารถหาวิธีที่จะบรรลุมันได้


นี่คือสถานการณ์ทั้งหมดที่ฉันต้องการนำไปใช้:

  • ผู้ใช้ไปที่เว็บไซต์ของฉันและเลือกรูปภาพผ่านinput type="file"องค์ประกอบ
  • ภาพนี้เรียกดูผ่าน JavaScript เราทำการตรวจสอบบางอย่างเช่นรูปแบบไฟล์ที่ถูกต้องขนาดไฟล์สูงสุดเป็นต้น
  • หากทุกอย่างเรียบร้อยจะมีการแสดงภาพตัวอย่างบนหน้า
  • ผู้ใช้สามารถดำเนินการขั้นพื้นฐานบางอย่างเช่นหมุนภาพ 90 ° / -90 °ครอบตัดตามอัตราส่วนที่กำหนดไว้ล่วงหน้า ฯลฯ หรือผู้ใช้สามารถอัปโหลดภาพอื่นและกลับไปที่ขั้นตอนที่ 1
  • เมื่อผู้ใช้พอใจภาพที่แก้ไขจะถูกบีบอัดและ "บันทึก" ไว้ในเครื่อง (ไม่ได้บันทึกลงในไฟล์ แต่อยู่ในหน่วยความจำ / หน้าของเบราว์เซอร์) -
  • ผู้ใช้กรอกแบบฟอร์มด้วยข้อมูลเช่นชื่ออายุ ฯลฯ
  • ผู้ใช้คลิกที่ปุ่ม "เสร็จสิ้น" จากนั้นแบบฟอร์มที่มีข้อมูล + รูปภาพที่บีบอัดจะถูกส่งไปยังเซิร์ฟเวอร์ (ไม่มี AJAX)

กระบวนการทั้งหมดจนถึงขั้นตอนสุดท้ายควรทำในฝั่งไคลเอ็นต์และควรเข้ากันได้กับ Chrome และ Firefox, Safari 5+ และIE 8+รุ่นล่าสุด ถ้าเป็นไปได้ควรใช้เฉพาะ JavaScript เท่านั้น (แต่ฉันค่อนข้างมั่นใจว่าเป็นไปไม่ได้)

ฉันไม่ได้เขียนโค้ดอะไรในตอนนี้ แต่ฉันได้คิดถึงมันแล้ว การอ่านไฟล์ในเครื่องสามารถทำได้ผ่านFile API การดูตัวอย่างและแก้ไขรูปภาพทำได้โดยใช้องค์ประกอบCanvasแต่ฉันหาวิธีทำส่วนบีบอัดรูปภาพไม่ได้

ตามhtml5please.comและcaniuse.comสนับสนุนเบราว์เซอร์เหล่านั้นเป็นเรื่องที่ค่อนข้างยาก (ขอบคุณ IE) แต่สามารถทำได้โดยใช้ polyfill เช่นFlashCanvasและFileReader

จริงๆแล้วเป้าหมายคือการลดขนาดไฟล์ดังนั้นฉันจึงเห็นว่าการบีบอัดภาพเป็นวิธีแก้ปัญหา แต่ฉันรู้ว่ารูปภาพที่อัปโหลดจะปรากฏบนเว็บไซต์ของฉันทุกครั้งในที่เดียวกันและฉันรู้ขนาดของพื้นที่แสดงผลนี้ (เช่น 200x400) ดังนั้นฉันสามารถปรับขนาดภาพให้พอดีกับขนาดเหล่านั้นได้ซึ่งจะลดขนาดไฟล์ลง ฉันไม่รู้ว่าอัตราส่วนการบีบอัดของเทคนิคนี้จะเป็นเท่าใด

คุณคิดอย่างไร ? คุณมีคำแนะนำที่จะบอกฉัน? คุณรู้วิธีบีบอัดรูปภาพฝั่งเบราว์เซอร์ใน JavaScript หรือไม่? ขอบคุณสำหรับการตอบกลับ

คำตอบ:


166

ในระยะสั้น:

  • อ่านไฟล์โดยใช้ HTML5 FileReader API ด้วย .readAsArrayBuffer
  • สร้าง Blob ด้วยข้อมูลไฟล์และรับ url ด้วยwindow.URL.createObjectURL (blob)
  • สร้างองค์ประกอบรูปภาพใหม่และตั้งค่าเป็น src เป็น URL ของไฟล์หยด
  • ส่งภาพไปที่แคนวาส ขนาดผ้าใบถูกกำหนดเป็นขนาดเอาต์พุตที่ต้องการ
  • รับข้อมูลที่ลดขนาดลงจาก canvas ผ่าน canvas.toDataURL ("image / jpeg", 0.7) (ตั้งค่ารูปแบบและคุณภาพเอาต์พุตของคุณเอง)
  • แนบอินพุตใหม่ที่ซ่อนอยู่ในรูปแบบเดิมและถ่ายโอนภาพ dataURI โดยทั่วไปเป็นข้อความปกติ
  • ในแบ็กเอนด์อ่าน dataURI ถอดรหัสจาก Base64 และบันทึก

ที่มา: code .


1
ขอบคุณมาก ! นี่คือสิ่งที่ฉันกำลังมองหา คุณรู้หรือไม่ว่าเทคนิคนี้อัตราส่วนกำลังอัดดีแค่ไหน?
pomeh

2
@NicholasKyriakides ฉันยืนยันได้ว่าcanvas.toDataURL("image/jpeg",0.7)บีบอัดได้อย่างมีประสิทธิภาพมันบันทึก JPEG ด้วยคุณภาพ 70 (เมื่อเทียบกับค่าเริ่มต้นคุณภาพ 100)
user1111929

4
@Nicholas Kyriakides นี่ไม่ใช่ความแตกต่างที่ดีที่จะทำ ตัวแปลงสัญญาณส่วนใหญ่ไม่มีการสูญเสียดังนั้นจึงเหมาะสมกับคำจำกัดความ "การลดขนาด" ของคุณ (กล่าวคือคุณไม่สามารถเปลี่ยนกลับเป็น 100 ได้)
Billybobbonnet

5
การลดขนาดหมายถึงการทำให้ภาพมีขนาดเล็กลงในแง่ของความสูงและความกว้าง นี่คือการบีบอัดจริงๆ มันเป็นการบีบอัดที่สูญเสีย แต่การบีบอัดที่แน่นอน ไม่ใช่การลดขนาดพิกเซล แต่เพียงแค่ขยับพิกเซลบางส่วนให้เป็นสีเดียวกันเพื่อให้การบีบอัดสามารถตีสีเหล่านั้นได้ในจำนวนบิตน้อยลง JPEG มีการบีบอัดพิกเซลในตัวอยู่แล้ว แต่ในโหมด lossy จะบอกว่าปิดไม่กี่สีสามารถเรียกได้ว่าเป็นสีเดียวกัน ที่ยังคงบีบอัด การลดขนาดที่เกี่ยวข้องกับกราฟิกมักหมายถึงการเปลี่ยนแปลงขนาดจริง
Tatarize

3
ฉันแค่อยากจะบอกสิ่งนี้: ไฟล์สามารถตรงไปที่URL.createObjectUrl()โดยไม่ต้องเปลี่ยนไฟล์เป็นหยด; ไฟล์จะนับเป็นหยด
hellol11

20

ฉันเห็นสองสิ่งที่ขาดหายไปจากคำตอบอื่น ๆ :

  • canvas.toBlob(เมื่อมี) มีประสิทธิภาพมากกว่าcanvas.toDataURLและ 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(...);
    });
};

ToBlob ทำเคล็ดลับให้ฉันสร้างไฟล์และรับในอาร์เรย์ $ _FILES บนเซิร์ฟเวอร์ ขอบคุณ!
Ricardo Ruiz Romero

ดูดี! แต่จะใช้ได้กับเบราว์เซอร์เว็บและมือถือทั้งหมดหรือไม่ (ละเว้น IE)
Garvit Jain

14

คำตอบของ @PsychoWoods เป็นสิ่งที่ดี ขอเสนอทางออกของตัวเอง ฟังก์ชัน Javascript นี้ใช้ URL ข้อมูลภาพและความกว้างปรับขนาดตามความกว้างใหม่และส่งคืน URL ข้อมูลใหม่

// 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 ข้อมูลและต้องการ URL ข้อมูลสำหรับรูปภาพที่ลดขนาด


โปรดให้รายละเอียดเพิ่มเติมเกี่ยวกับตัวอย่างนี้วิธีการเรียกใช้ฟังก์ชันและผลลัพธ์ที่ได้รับอย่างไร
visulo

นี่คือตัวอย่าง: danielsadventure.info/Html/scaleimage.htmlอย่าลืมอ่านแหล่งที่มาของหน้าเพื่อดูวิธีการทำงาน
Vivian River

1
web.archive.org/web/20171226190510/danielsadventure.info/Html/… สำหรับคนอื่น ๆ ที่ต้องการอ่านลิงค์ที่เสนอโดย @DanielAllenLangdon
Cedric Arnould

ในบางครั้ง image.width / height จะคืนค่าเป็น 0 เนื่องจากยังไม่ได้โหลด คุณอาจต้องแปลงสิ่งนี้เป็นฟังก์ชัน async และฟัง image.onload เพื่อให้ได้ภาพที่ถูกต้องและมีความสูง
Matt Pope

9

คุณสามารถดูการแปลงรูปภาพลองที่นี่ -> หน้าสาธิต

ป้อนคำอธิบายภาพที่นี่


1
โปรดเพิ่มข้อมูลเกี่ยวกับแหล่งข้อมูลที่เชื่อมโยง
SanSolo

คำตอบนี้เป๊ะ!
djcaesar9114

ลิงก์หน้าสาธิตใช้งานไม่ได้ คุณสามารถทดสอบได้ที่นี่: demo.wangyulue.com/image-conversion
gabrielstuff

4

ผมมีปัญหากับdownscaleImage()ฟังก์ชั่นโพสต์ข้างต้นโดย @ แดเนียลอัลเลน-แลงดอนในที่image.widthและimage.heightคุณสมบัติจะไม่สามารถใช้ได้ทันทีเพราะโหลดภาพที่ไม่ตรงกัน

โปรดดูตัวอย่าง TypeScript ที่อัปเดตด้านล่างซึ่งคำนึงถึงสิ่งนี้ใช้asyncฟังก์ชันและปรับขนาดภาพตามขนาดที่ยาวที่สุดแทนที่จะเป็นเพียงความกว้าง

function getImage(dataUrl: string): Promise<HTMLImageElement> 
{
    return new Promise((resolve, reject) => {
        const image = new Image();
        image.src = dataUrl;
        image.onload = () => {
            resolve(image);
        };
        image.onerror = (el: any, err: ErrorEvent) => {
            reject(err.error);
        };
    });
}

export async function downscaleImage(
        dataUrl: string,  
        imageType: string,  // e.g. 'image/jpeg'
        resolution: number,  // max width/height in pixels
        quality: number   // e.g. 0.9 = 90% quality
    ): Promise<string> {

    // Create a temporary image so that we can compute the height of the image.
    const image = await getImage(dataUrl);
    const oldWidth = image.naturalWidth;
    const oldHeight = image.naturalHeight;
    console.log('dims', oldWidth, oldHeight);

    const longestDimension = oldWidth > oldHeight ? 'width' : 'height';
    const currentRes = longestDimension == 'width' ? oldWidth : oldHeight;
    console.log('longest dim', longestDimension, currentRes);

    if (currentRes > resolution) {
        console.log('need to resize...');

        // Calculate new dimensions
        const newSize = longestDimension == 'width'
            ? Math.floor(oldHeight / oldWidth * resolution)
            : Math.floor(oldWidth / oldHeight * resolution);
        const newWidth = longestDimension == 'width' ? resolution : newSize;
        const newHeight = longestDimension == 'height' ? resolution : newSize;
        console.log('new width / height', newWidth, newHeight);

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

        // Draw the downscaled image on the canvas and return the new data URL.
        const ctx = canvas.getContext('2d')!;
        ctx.drawImage(image, 0, 0, newWidth, newHeight);
        const newDataUrl = canvas.toDataURL(imageType, quality);
        return newDataUrl;
    }
    else {
        return dataUrl;
    }

}

ฉันจะเพิ่มคำอธิบายของquality, resolutionและimageType(รูปแบบนี้)
Barabas

3

แก้ไข: ตามความคิดเห็นของ Mr Me ในคำตอบนี้ดูเหมือนว่าขณะนี้การบีบอัดพร้อมใช้งานสำหรับรูปแบบ JPG / WebP แล้ว (ดูhttps://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL ) .

เท่าที่ฉันทราบคุณไม่สามารถบีบอัดรูปภาพโดยใช้แคนวาสได้ แต่คุณสามารถปรับขนาดได้ การใช้ canvas.toDataURL จะไม่ให้คุณเลือกอัตราส่วนการบีบอัดที่จะใช้ คุณสามารถดู canimage ที่ทำในสิ่งที่คุณต้องการได้ที่https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js

ในความเป็นจริงมักจะเพียงพอที่จะปรับขนาดภาพเพื่อลดขนาด แต่ถ้าคุณต้องการไปไกลกว่านั้นคุณจะต้องใช้ method file.readAsArrayBuffer ที่เพิ่งเปิดตัวใหม่เพื่อรับบัฟเฟอร์ที่มีข้อมูลรูปภาพ

จากนั้นใช้ DataView เพื่ออ่านเนื้อหาตามข้อกำหนดรูปแบบภาพ ( http://en.wikipedia.org/wiki/JPEGหรือhttp://en.wikipedia.org/wiki/Portable_Network_Graphics )

มันจะยากที่จะจัดการกับการบีบอัดข้อมูลภาพ แต่มันแย่กว่านั้นถ้าลอง ในทางกลับกันคุณสามารถลองลบส่วนหัว PNG หรือข้อมูล JPEG exif เพื่อทำให้ภาพของคุณมีขนาดเล็กลงควรจะทำได้ง่ายกว่า

คุณจะต้องสร้าง DataWiew อื่นบนบัฟเฟอร์อื่นและเติมด้วยเนื้อหารูปภาพที่กรองแล้ว จากนั้นคุณจะต้องเข้ารหัสเนื้อหารูปภาพของคุณเป็น DataURI โดยใช้ window.btoa

แจ้งให้เราทราบหากคุณใช้สิ่งที่คล้ายกันจะน่าสนใจในการอ่านโค้ด


อาจมีบางอย่างเปลี่ยนแปลงไปตั้งแต่คุณโพสต์สิ่งนี้ แต่อาร์กิวเมนต์ที่สองของฟังก์ชันcanvas.toDataURL ที่คุณกล่าวถึงคือจำนวนการบีบอัดที่คุณต้องการใช้
wp-overwatch.com

2

ฉันพบว่ามีวิธีแก้ปัญหาที่ง่ายกว่าเมื่อเทียบกับคำตอบที่ยอมรับ

  • อ่านไฟล์โดยใช้ HTML5 FileReader API ด้วย .readAsArrayBuffer
  • สร้าง Blob ด้วยข้อมูลไฟล์และรับ url ด้วย window.URL.createObjectURL(blob)
  • สร้างองค์ประกอบรูปภาพใหม่และตั้งค่าเป็น src เป็น URL ของไฟล์หยด
  • ส่งภาพไปที่แคนวาส ขนาดผ้าใบถูกกำหนดเป็นขนาดเอาต์พุตที่ต้องการ
  • รับข้อมูลที่ลดขนาดลงจากผ้าใบผ่านcanvas.toDataURL("image/jpeg",0.7)(ตั้งค่ารูปแบบและคุณภาพผลลัพธ์ของคุณเอง)
  • แนบอินพุตใหม่ที่ซ่อนอยู่ในรูปแบบเดิมและถ่ายโอนภาพ dataURI โดยทั่วไปเป็นข้อความปกติ
  • ในแบ็กเอนด์อ่าน dataURI ถอดรหัสจาก Base64 และบันทึก

ตามคำถามของคุณ:

มีวิธีบีบอัดรูปภาพ (ส่วนใหญ่เป็น jpeg, png และ gif) โดยตรงทางเบราว์เซอร์ก่อนที่จะอัปโหลด

วิธีแก้ปัญหาของฉัน:

  1. สร้างหยดด้วยไฟล์โดยตรงด้วยURL.createObjectURL(inputFileElement.files[0]).

  2. เหมือนกับคำตอบที่ยอมรับ

  3. เหมือนกับคำตอบที่ยอมรับ มูลค่าการกล่าวขวัญว่าขนาดผืนผ้าใบเป็นสิ่งที่จำเป็นและการใช้งานimg.widthและimg.heightการตั้งค่าและcanvas.width ไม่canvas.heightimg.clientWidth

  4. canvas.toBlob(callbackfunction(blob){}, 'image/jpeg', 0.5)รับภาพขนาดลดลง การตั้งค่า'image/jpg'ไม่มีผล image/pngยังได้รับการสนับสนุน สร้างใหม่Fileวัตถุภายในcallbackfunction ร่างกายlet compressedImageBlob = new File([blob])ด้วย

  5. เพิ่มปัจจัยการผลิตที่ซ่อนอยู่ใหม่หรือส่งผ่านทางจาวาสคริปต์ เซิร์ฟเวอร์ไม่ต้องถอดรหัสอะไรเลย

ตรวจสอบhttps://javascript.info/binaryสำหรับข้อมูลทั้งหมด ฉันคิดวิธีแก้ปัญหาหลังจากอ่านบทนี้


รหัส:

    <!DOCTYPE html>
    <html>
    <body>
    <form action="upload.php" method="post" enctype="multipart/form-data">
      Select image to upload:
      <input type="file" name="fileToUpload" id="fileToUpload" multiple>
      <input type="submit" value="Upload Image" name="submit">
    </form>
    </body>
    </html>

รหัสนี้ดูน่ากลัวน้อยกว่าคำตอบอื่น ๆ ..

อัปเดต:

img.onloadหนึ่งมีการใส่ในทุกอย่าง มิฉะนั้นcanvasจะไม่สามารถรับความกว้างและความสูงของภาพได้อย่างถูกต้องตามเวลาที่canvasกำหนด

    function upload(){
        var f = fileToUpload.files[0];
        var fileName = f.name.split('.')[0];
        var img = new Image();
        img.src = URL.createObjectURL(f);
        img.onload = function(){
            var canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            var ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            canvas.toBlob(function(blob){
                    console.info(blob.size);
                    var f2 = new File([blob], fileName + ".jpeg");
                    var xhr = new XMLHttpRequest();
                    var form = new FormData();
                    form.append("fileToUpload", f2);
                    xhr.open("POST", "upload.php");
                    xhr.send(form);
            }, 'image/jpeg', 0.5);
        }
    }

3.4MB .pngทดสอบการบีบอัดไฟล์ด้วยimage/jpegชุดอาร์กิวเมนต์

    |0.9| 777KB |
    |0.8| 383KB |
    |0.7| 301KB |
    |0.6| 251KB |
    |0.5| 219kB |

0

สำหรับการบีบอัดรูปภาพ JPG คุณสามารถใช้เทคนิคการบีบอัดที่ดีที่สุดที่เรียกว่า JIC (Javascript Image Compression) สิ่งนี้จะช่วยคุณได้อย่างแน่นอน -> https://github.com/brunobar79/JIC


1
โดยไม่ลดคุณภาพ
lifeisbeautiful

0

ฉันปรับปรุงฟังก์ชั่นส่วนหัวให้เป็น:

var minifyImg = function(dataUrl,newWidth,imageType="image/jpeg",resolve,imageArguments=0.7){
    var image, oldWidth, oldHeight, newHeight, canvas, ctx, newDataUrl;
    (new Promise(function(resolve){
      image = new Image(); image.src = dataUrl;
      log(image);
      resolve('Done : ');
    })).then((d)=>{
      oldWidth = image.width; oldHeight = image.height;
      log([oldWidth,oldHeight]);
      newHeight = Math.floor(oldHeight / oldWidth * newWidth);
      log(d+' '+newHeight);

      canvas = document.createElement("canvas");
      canvas.width = newWidth; canvas.height = newHeight;
      log(canvas);
      ctx = canvas.getContext("2d");
      ctx.drawImage(image, 0, 0, newWidth, newHeight);
      //log(ctx);
      newDataUrl = canvas.toDataURL(imageType, imageArguments);
      resolve(newDataUrl);
    });
  };

การใช้งาน:

minifyImg(<--DATAURL_HERE-->,<--new width-->,<--type like image/jpeg-->,(data)=>{
   console.log(data); // the new DATAURL
});

สนุก ;)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.