รับ URL ข้อมูลภาพใน JavaScript?


335

ฉันมีหน้า HTML ปกติพร้อมภาพบางภาพ (แค่<img />แท็ก HTML ปกติ) ฉันต้องการรับเนื้อหาของพวกเขา base64 เข้ารหัสดีกว่าโดยไม่จำเป็นต้องดาวน์โหลดภาพใหม่ (เช่นเบราว์เซอร์โหลดแล้วดังนั้นตอนนี้ฉันต้องการเนื้อหา)

ฉันชอบที่จะประสบความสำเร็จกับ Greasemonkey และ Firefox

คำตอบ:


400

หมายเหตุ:ใช้งานได้เฉพาะเมื่อภาพนั้นมาจากโดเมนเดียวกันกับหน้าเว็บหรือมีcrossOrigin="anonymous"แอตทริบิวต์และเซิร์ฟเวอร์รองรับ CORS มันจะไม่ให้ไฟล์ต้นฉบับกับคุณ แต่เป็นเวอร์ชันที่เข้ารหัสใหม่ หากคุณต้องการผลที่ตามมาจะเป็นเหมือนเดิมให้ดูคำตอบของ Kaiido


คุณจะต้องสร้างองค์ประกอบผ้าใบที่มีขนาดที่ถูกต้องและคัดลอกข้อมูลภาพด้วยdrawImageฟังก์ชั่น จากนั้นคุณสามารถใช้toDataURLฟังก์ชันเพื่อรับข้อมูล: url ที่มีอิมเมจเข้ารหัส -64 โปรดทราบว่าภาพนั้นจะต้องโหลดอย่างสมบูรณ์หรือคุณจะกลับมาเป็นภาพว่างเปล่า (สีดำโปร่งใส)

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

function getBase64Image(img) {
    // Create an empty canvas element
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;

    // Copy the image contents to the canvas
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);

    // Get the data-URL formatted image
    // Firefox supports PNG and JPEG. You could check img.src to
    // guess the original format, but be aware the using "image/jpg"
    // will re-encode the image.
    var dataURL = canvas.toDataURL("image/png");

    return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}

การรับภาพที่จัดรูปแบบ JPEG ไม่สามารถใช้งานได้กับ Firefox รุ่นเก่า (ประมาณ 3.5) ดังนั้นหากคุณต้องการสนับสนุนคุณจะต้องตรวจสอบความเข้ากันได้ หากไม่รองรับการเข้ารหัสมันจะเริ่มต้นที่ "image / png"


1
ขณะนี้ดูเหมือนว่าจะทำงาน (ยกเว้น unescaped / ในคืน) มันไม่ได้สร้างสตริง base64 เดียวกับที่ฉันได้รับจาก PHP เมื่อทำ base64_encode ในไฟล์ที่ได้รับด้วยฟังก์ชั่น file_get_contents ภาพดูเหมือนคล้ายกันมาก / เหมือนกัน แต่ก็ยังคงเป็น Javascripted ที่เล็กกว่าและฉันก็รักมันเหมือนกันทุกประการ อีกสิ่งหนึ่ง: ภาพอินพุตมีขนาดเล็ก (594 ไบต์), 28x30 PNG ที่มีพื้นหลังโปร่งใส - หากมีการเปลี่ยนแปลงอะไร
Detariael

7
Firefox อาจใช้ระดับการบีบอัดที่แตกต่างกันซึ่งจะส่งผลต่อการเข้ารหัส นอกจากนี้ฉันคิดว่า PNG สนับสนุนข้อมูลส่วนหัวพิเศษบางอย่างเช่นโน้ตซึ่งอาจหายไปเนื่องจากผืนผ้าใบได้รับข้อมูลพิกเซลเท่านั้น หากคุณต้องการให้เหมือนกันคุณอาจใช้ AJAX เพื่อรับไฟล์และ base64 เข้ารหัสด้วยตนเอง
Matthew Crumley

7
ใช่คุณจะดาวน์โหลดภาพด้วย XMLHttpRequest หวังว่ามันจะใช้อิมเมจเวอร์ชันแคช แต่จะขึ้นอยู่กับการกำหนดค่าเซิร์ฟเวอร์และเบราว์เซอร์และคุณจะต้องมีการร้องขอค่าใช้จ่ายเพื่อตรวจสอบว่าไฟล์มีการเปลี่ยนแปลง นั่นเป็นสาเหตุที่ฉันไม่ได้แนะนำในตอนแรก :-) แต่น่าเสียดายที่ฉันรู้ว่ามันเป็นวิธีเดียวที่จะได้รับไฟล์ต้นฉบับ
Matthew Crumley

2
@trusktr ความdrawImageมุ่งมั่นไม่ทำอะไรเลยและคุณจะพบกับผืนผ้าใบว่างเปล่าและภาพที่ปรากฏ
Matthew Crumley

1
@JohnSewell นั่นเป็นเพราะ OP ต้องการเนื้อหาไม่ใช่ URL คุณจะต้องข้ามการแทนที่ (... ) หากคุณต้องการใช้เป็นแหล่งภาพ
Matthew Crumley

76

ฟังก์ชันนี้ใช้ URL จากนั้นส่งคืนอิมเมจ BASE64

function getBase64FromImageUrl(url) {
    var img = new Image();

    img.setAttribute('crossOrigin', 'anonymous');

    img.onload = function () {
        var canvas = document.createElement("canvas");
        canvas.width =this.width;
        canvas.height =this.height;

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

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

        alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
    };

    img.src = url;
}

เรียกว่าเป็นแบบนี้: getBase64FromImageUrl("images/slbltxt.png")


8
มีวิธีการเปิดภาพจากลิงค์ภายนอกไปยังฐาน 64 หรือไม่
dinodsaurus

@dinodsaurus คุณสามารถโหลดได้ในแท็กรูปภาพหรือทำแบบสอบถาม ajax
Jared Forsyth

6
มันต้องให้พวกมันใช้ CORS แต่คุณต้องทำ $ .ajax (theimageurl) และมันควรจะคืนสิ่งที่สมเหตุสมผล มิฉะนั้น (หากพวกเขาไม่ได้เปิดใช้งาน CORS) มันจะไม่ทำงาน รูปแบบความปลอดภัยของอินเทอร์เน็ตไม่อนุญาต เว้นแต่แน่นอนคุณอยู่ในปลั๊กอิน Chrome ซึ่งในกรณีนี้ทุกอย่างจะได้รับอนุญาตแม้ตัวอย่างข้างต้น
Jared Forsyth

4
คุณต้องใส่img.src =หลังจากimg.onload =เพราะในบางเบราว์เซอร์เช่น Opera เหตุการณ์จะไม่เกิดขึ้น
ostapische

1
@Hakkar การรั่วไหลของหน่วยความจำจะเกิดขึ้นในเบราว์เซอร์เก่าที่ใช้การอ้างอิงยังคงนับเท่านั้นเพื่อแจ้งการรวบรวมขยะ เพื่อความเข้าใจของฉันการอ้างอิงแบบวงกลมไม่ได้สร้างการรั่วไหลในเครื่องหมายและการตั้งค่าการกวาด เมื่อขอบเขตของฟังก์ชั่นgetBase64FromImageUrl(url)และimg.onload = function ()ออกจาก img นั้นไม่สามารถเข้าถึงได้และเก็บขยะ นอกเหนือจาก IE 6/7 และนี่ก็โอเค
humanityANDpeace

59

อีกไม่นานหลังจากนี้ แต่ไม่มีคำตอบใดที่ถูกต้องทั้งหมด

เมื่อวาดบนผืนผ้าใบรูปภาพที่ผ่านจะไม่ถูกบีบอัด + ทั้งหมดถูกคูณด้วย
เมื่อส่งออกจะไม่มีการบีบอัดหรือบีบอัดด้วยอัลกอริทึมที่แตกต่างกันและไม่ถูกคูณ

เบราว์เซอร์และอุปกรณ์ทั้งหมดจะมีข้อผิดพลาดในการปัดเศษที่แตกต่างกันเกิดขึ้นในกระบวนการนี้
(ดูที่ลายนิ้วมือ Canvas )

ดังนั้นหากต้องการไฟล์ภาพรุ่น base64 พวกเขาจะต้องขออีกครั้ง (ส่วนใหญ่จะมาจากแคช) แต่ครั้งนี้เป็น Blob

จากนั้นคุณสามารถใช้FileReaderเพื่ออ่านเป็น ArrayBuffer หรือเป็น dataURL

function toDataURL(url, callback){
    var xhr = new XMLHttpRequest();
    xhr.open('get', url);
    xhr.responseType = 'blob';
    xhr.onload = function(){
      var fr = new FileReader();
    
      fr.onload = function(){
        callback(this.result);
      };
    
      fr.readAsDataURL(xhr.response); // async call
    };
    
    xhr.send();
}

toDataURL(myImage.src, function(dataURL){
  result.src = dataURL;

  // now just to show that passing to a canvas doesn't hold the same results
  var canvas = document.createElement('canvas');
  canvas.width = myImage.naturalWidth;
  canvas.height = myImage.naturalHeight;
  canvas.getContext('2d').drawImage(myImage, 0,0);

  console.log(canvas.toDataURL() === dataURL); // false - not same data
  });
<img id="myImage" src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" crossOrigin="anonymous">
<img id="result">


เพียงแค่ผ่านไปแล้วเช่น 5 + คำถามที่แตกต่างกันและรหัสของคุณเป็นเพียงคนเดียวที่ทำงานได้อย่างถูกต้องขอบคุณ :)
ปีเตอร์

1
ฉันพบข้อผิดพลาดพร้อมรหัสด้านบน XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access.
STWilson

2
@STWilson ใช่วิธีนี้เชื่อมโยงกับนโยบายต้นทางเดียวกันเช่นเดียวกับพื้นที่วาดภาพ ในกรณีที่มีการร้องขอข้ามต้นทางคุณต้องกำหนดค่าเซิร์ฟเวอร์การโฮสต์เพื่ออนุญาตคำขอข้ามต้นทางดังกล่าวให้กับสคริปต์ของคุณ
Kaiido

@Kaiido ดังนั้นหากภาพอยู่บนเซิร์ฟเวอร์อื่นนอกเหนือจากสคริปต์เซิร์ฟเวอร์ที่โฮสต์ต้องเปิดใช้งานคำขอข้ามต้นทางหรือไม่ หากคุณตั้ง img.setAttribute ('crossOrigin', 'anonymous') จะป้องกันข้อผิดพลาดหรือไม่?
1.21 gigawatts

1
เมื่อการวิจัยมากขึ้นดูเหมือนว่าของภาพที่สามารถใช้แอตทริบิวต์ crossOrigin เพื่อขอสิทธิ์การเข้าถึง แต่ก็ขึ้นอยู่กับเซิร์ฟเวอร์โฮสติ้งเพื่อให้การเข้าถึงผ่านหัวล ธ , sitepoint.com/an-in-depth-look-at-cors สำหรับ XMLHttpRequest ดูเหมือนว่าเซิร์ฟเวอร์จะต้องให้สิทธิ์การเข้าถึงเหมือนกับรูปภาพผ่านส่วนหัว CORS แต่ไม่จำเป็นต้องมีการเปลี่ยนแปลงในรหัสของคุณยกเว้นอาจตั้งค่า xhr.withCredentials เป็นเท็จ (ค่าเริ่มต้น)
1.21 gigawatts

20

คำตอบของ kaiido ที่ทันสมัยยิ่งขึ้นเมื่อใช้การดึงข้อมูลคือ:

function toObjectUrl(url) {
  return fetch(url)
      .then((response)=> {
        return response.blob();
      })
      .then(blob=> {
        return URL.createObjectURL(blob);
      });
}

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

แก้ไข: ตามที่ระบุไว้ในความคิดเห็นสิ่งนี้จะส่งคืน URL วัตถุที่ชี้ไปที่ไฟล์ในระบบท้องถิ่นของคุณแทน DataURL จริงดังนั้นขึ้นอยู่กับกรณีการใช้งานของคุณนี่อาจไม่ใช่สิ่งที่คุณต้องการ

คุณสามารถดูคำตอบต่อไปนี้เพื่อใช้การดึงข้อมูลและ dataURL จริง: https://stackoverflow.com/a/50463054/599602


2
อย่าลืมโทรURL.revokeObjectURL()หากคุณมีแอปพลิเคชั่นที่ใช้งานมานานโดยใช้วิธีนี้มิฉะนั้นหน่วยความจำของคุณจะรก
วิศวกร

1
ข้อควรสนใจ: DataURL (เข้ารหัส 64) ไม่เหมือนกับ ObjectURL (อ้างอิงจาก blob)
DaniEll

1
ObjectURL ไม่ใช่ URL ของข้อมูลอย่างแน่นอน นี่ไม่ใช่คำตอบที่ถูกต้อง
Danny Lin

2

shiv / shim / sham

หากรูปภาพของคุณโหลดแล้ว (หรือไม่) เครื่องมือ "" นี้อาจมีประโยชน์:

Object.defineProperty
(
    HTMLImageElement.prototype,'toDataURL',
    {enumerable:false,configurable:false,writable:false,value:function(m,q)
    {
        let c=document.createElement('canvas');
        c.width=this.naturalWidth; c.height=this.naturalHeight;
        c.getContext('2d').drawImage(this,0,0); return c.toDataURL(m,q);
    }}
);

.. แต่ทำไม

นี่เป็นข้อได้เปรียบของการใช้ข้อมูลภาพที่ "โหลดแล้ว" ดังนั้นจึงไม่จำเป็นต้องมีคำขอพิเศษ Aditionally จะช่วยให้ผู้ใช้ปลายทาง (โปรแกรมเมอร์เช่นคุณ) ตัดสินใจและ / หรือmime-typeและqualityคัดลอกคุณสามารถออกจากการขัดแย้งเหล่านี้ / พารามิเตอร์ที่อธิบายไว้ในMDNสเปคที่นี่

หากคุณโหลด JS นี้ (ก่อนหน้านี้เมื่อต้องการ) การแปลงdataURLเป็นไฟล์ง่ายๆนั้น:

ตัวอย่าง

HTML
<img src="/yo.jpg" onload="console.log(this.toDataURL('image/jpeg'))">
JS
console.log(document.getElementById("someImgID").toDataURL());

พิมพ์ลายนิ้วมือ GPU

หากคุณกังวลเกี่ยวกับ "ความแม่นยำ" ของบิตคุณสามารถแก้ไขเครื่องมือนี้เพื่อให้เหมาะกับความต้องการของคุณตามคำตอบของ @ Kaiido


1

ใช้onloadเหตุการณ์เพื่อแปลงภาพหลังจากโหลด


-3

ใน HTML5 ควรใช้สิ่งนี้:

{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}

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