Lấy url dữ liệu hình ảnh trong JavaScript?


335

Tôi có một trang HTML thông thường với một số hình ảnh (chỉ các <img />thẻ HTML thông thường ). Tôi muốn có được nội dung của họ, tốt nhất là mã hóa 64, mà không cần tải lại hình ảnh (tức là nó đã được trình duyệt tải, vì vậy bây giờ tôi muốn nội dung).

Tôi muốn đạt được điều đó với Greasemonkey và Firefox.

Câu trả lời:


400

Lưu ý: Điều này chỉ hoạt động nếu hình ảnh từ cùng một tên miền với trang hoặc có crossOrigin="anonymous"thuộc tính và máy chủ hỗ trợ CORS. Nó cũng sẽ không cung cấp cho bạn tệp gốc mà là phiên bản được mã hóa lại. Nếu bạn cần kết quả giống hệt với bản gốc, hãy xem câu trả lời của Kaiido .


Bạn sẽ cần tạo một thành phần canvas với kích thước chính xác và sao chép dữ liệu hình ảnh với drawImagechức năng. Sau đó, bạn có thể sử dụng toDataURLchức năng để lấy dữ liệu: url có hình ảnh được mã hóa cơ sở 64. Lưu ý rằng hình ảnh phải được tải đầy đủ, hoặc bạn sẽ chỉ nhận lại được một hình ảnh trống (đen, trong suốt).

Nó sẽ là một cái gì đó như thế này. Tôi chưa bao giờ viết tập lệnh Greasemonkey, vì vậy bạn có thể cần điều chỉnh mã để chạy trong môi trường đó.

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,/, "");
}

Bắt hình ảnh có định dạng JPEG không hoạt động trên các phiên bản cũ hơn (khoảng 3,5) của Firefox, vì vậy nếu bạn muốn hỗ trợ điều đó, bạn sẽ cần kiểm tra tính tương thích. Nếu mã hóa không được hỗ trợ, nó sẽ mặc định là "image / png".


1
Mặc dù điều này dường như đang hoạt động (ngoại trừ không trả lời / trả lại), nhưng nó không tạo ra chuỗi cơ sở 64 giống như chuỗi tôi nhận được từ PHP khi thực hiện base64_encode trên tệp có được với hàm file_get_contents. Các hình ảnh có vẻ rất giống nhau / giống nhau, vẫn là hình ảnh Javascript nhỏ hơn và tôi thích chúng giống hệt nhau. Một điều nữa: hình ảnh đầu vào là một hình ảnh nhỏ (594 byte), 28x30 PNG với nền trong suốt - nếu điều đó thay đổi bất cứ điều gì.
Detariael

7
Firefox có thể đang sử dụng một mức nén khác nhau sẽ ảnh hưởng đến mã hóa. Ngoài ra, tôi nghĩ rằng PNG hỗ trợ một số thông tin tiêu đề bổ sung như ghi chú, sẽ bị mất, vì khung vẽ chỉ nhận được dữ liệu pixel. Nếu bạn cần nó giống hệt nhau, có lẽ bạn có thể sử dụng AJAX để lấy tệp và mã hóa64 theo cách thủ công.
Matthew Crumley

7
Có, bạn sẽ tải xuống hình ảnh với XMLHttpRequest. Hy vọng, nó sẽ sử dụng phiên bản được lưu trong bộ nhớ cache của hình ảnh, nhưng điều đó phụ thuộc vào cấu hình máy chủ và trình duyệt và bạn sẽ có chi phí yêu cầu để xác định xem tệp có thay đổi hay không. Đó là lý do tại sao tôi không đề xuất rằng ở nơi đầu tiên :-) Thật không may, theo như tôi biết, đó là cách duy nhất để có được tệp gốc.
Matthew Crumley

2
@trusktr drawImageSẽ không làm gì cả, và bạn sẽ kết thúc với một khung vẽ trống và hình ảnh kết quả.
Matthew Crumley

1
@JohnSewell Điều đó chỉ vì OP muốn nội dung chứ không phải URL. Bạn sẽ cần bỏ qua cuộc gọi thay thế (...) nếu bạn muốn sử dụng nó làm nguồn hình ảnh.
Matthew Crumley

76

Hàm này lấy URL sau đó trả về hình ảnh 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;
}

Gọi nó như thế này: getBase64FromImageUrl("images/slbltxt.png")


8
Có cách nào để biến một hình ảnh từ một liên kết bên ngoài sang cơ sở 64 không?
dinodsaurus

@dinodsaurus bạn có thể tải nó trong thẻ hình ảnh hoặc thực hiện truy vấn ajax.
Jared Forsyth

6
Chà, nó yêu cầu họ thực hiện CORS, nhưng sau đó bạn chỉ cần làm $ .ajax (theimageurl) và nó sẽ trả về một cái gì đó hợp lý. Mặt khác (nếu chúng không kích hoạt CORS) thì nó sẽ không hoạt động; mô hình bảo mật của Internet không cho phép nó. Tất nhiên, trừ khi bạn ở trong một plugin chrome, trong trường hợp đó mọi thứ đều được cho phép - ngay cả ví dụ trên
Jared Forsyth

4
Bạn phải đặt img.src =sau img.onload =, vì trong một số trình duyệt, chẳng hạn như Opera, sự kiện sẽ không xảy ra.
Ostapische

1
@Hakkar rò rỉ bộ nhớ sẽ chỉ xảy ra trong các trình duyệt cũ sử dụng số tham chiếu vẫn để thông báo thu gom rác. Theo hiểu biết của tôi, tham chiếu vòng tròn không tạo ra rò rỉ trong một thiết lập đánh dấu và quét . Khi phạm vi của các chức năng getBase64FromImageUrl(url)img.onload = function ()thoát khỏi img không thể truy cập được và rác được thu thập. Khác với IE 6/7 và điều này là ok.
nhân

59

Đến rất lâu sau đó, nhưng không có câu trả lời nào ở đây là hoàn toàn chính xác.

Khi được vẽ trên một khung vẽ, hình ảnh đã qua không bị nén + tất cả được nhân lên trước.
Khi được xuất, nó không được nén hoặc nén lại với một thuật toán khác và không được nhân lên.

Tất cả các trình duyệt và thiết bị sẽ có các lỗi làm tròn khác nhau xảy ra trong quy trình này
(xem Dấu vân tay Canvas ).

Vì vậy, nếu ai đó muốn có một phiên bản cơ sở64 của một tệp hình ảnh, họ phải yêu cầu lại nó (phần lớn thời gian nó sẽ đến từ bộ đệm) nhưng lần này là một Blob.

Sau đó, bạn có thể sử dụng FileReader để đọc nó dưới dạng ArrayBuffer hoặc dưới dạng 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">


Chỉ cần trải qua hơn 5 câu hỏi khác nhau và mã của bạn là câu hỏi duy nhất hoạt động chính xác, cảm ơn :)
Peter

1
Tôi nhận được lỗi này với mã trên. XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access.
STWilson

2
@STWilson, vâng, phương pháp này cũng được gắn với chính sách cùng nguồn gốc, giống như chính sách canvas. Trong trường hợp yêu cầu xuất xứ chéo, bạn cần định cấu hình máy chủ lưu trữ để cho phép các yêu cầu xuất xứ chéo đó vào tập lệnh của bạn.
Kaiido

@Kaiido vậy nếu hình ảnh ở trên một máy chủ khác ngoài tập lệnh thì máy chủ lưu trữ phải kích hoạt các yêu cầu xuất xứ chéo? Nếu bạn đặt img.setAttribution ('crossOrigin', 'nặc danh') thì điều đó có ngăn được lỗi không?
1,21 gigawatt

1
Sau khi nghiên cứu thêm, có vẻ như hình ảnh có thể sử dụng thuộc tính crossOrigin để yêu cầu quyền truy cập nhưng tùy thuộc vào máy chủ lưu trữ để cấp quyền truy cập thông qua tiêu đề CORS, sitepoint.com/an-in-depth-look-at-cors . Đối với XMLHttpRequest, có vẻ như máy chủ phải cấp quyền truy cập giống như đối với hình ảnh thông qua tiêu đề CORS nhưng không yêu cầu thay đổi trong mã của bạn ngoại trừ có thể đặt xhr.withCredentials thành false (mặc định).
1,21 gigawatt

20

Một phiên bản hiện đại hơn của câu trả lời của kaiido bằng cách sử dụng fetch sẽ là:

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

Chỉnh sửa: Như đã nêu trong các nhận xét, điều này sẽ trả về một url đối tượng trỏ đến một tệp trong hệ thống cục bộ của bạn thay vì DataURL thực tế, do đó tùy thuộc vào trường hợp sử dụng của bạn, đây có thể không phải là thứ bạn cần.

Bạn có thể xem câu trả lời sau để sử dụng tìm nạp và dữ liệu thực tếURL: https://stackoverflow.com/a/50463054/599602


2
Đừng quên gọi URL.revokeObjectURL()nếu bạn có một ứng dụng chạy dài bằng phương pháp này, nếu không bộ nhớ của bạn sẽ bị lộn xộn.
Kỹ sư

1
Chú ý: DataURL (mã hóa base64) không giống với ObjectURL (tham chiếu đến blob)
DaniEll

1
ObjectURL chắc chắn không phải là URL dữ liệu. Đây không phải là một câu trả lời đúng.
Daniel Lin

2

shiv / shim / giả

Nếu (các) hình ảnh của bạn đã được tải (hoặc không), "công cụ" này có thể có ích:

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);
    }}
);

.. nhưng tại sao?

Điều này có lợi thế là sử dụng dữ liệu hình ảnh "đã được tải", do đó không cần thêm yêu cầu. Aditionally nó cho phép người dùng cuối (lập trình viên như bạn) quyết định CORS và / hoặc mime-typequality-HOẶC- bạn có thể bỏ qua những lập luận / thông số như mô tả trong MDN đặc điểm kỹ thuật ở đây .

Nếu bạn đã tải mã JS này (trước khi cần), thì việc chuyển đổi sang dataURLđơn giản như:

ví dụ

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

Dấu vân tay GPU

Nếu bạn lo lắng về "tính chính xác" của các bit thì bạn có thể thay đổi công cụ này cho phù hợp với nhu cầu của bạn như được cung cấp bởi câu trả lời của @ Kaiido.


1

Sử dụng onloadsự kiện để chuyển đổi hình ảnh sau khi tải


-3

Trong HTML5, sử dụng tốt hơn điều này:

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

2
Trong khi trả lời câu hỏi hãy cố gắng cụ thể hơn và cung cấp các giải thích chi tiết.
Piyush Zalani
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.