Làm thế nào để có được một tập tin hoặc blob từ một URL đối tượng?


127

Tôi đang cho phép người dùng tải hình ảnh vào một trang thông qua kéo và thả và các phương pháp khác. Khi một hình ảnh bị hủy, tôi đang sử dụng URL.createObjectURLđể chuyển đổi sang một URL đối tượng để hiển thị hình ảnh. Tôi không thu hồi url, vì tôi sử dụng lại nó.

Vì vậy, khi đến lúc tạo một FormDatađối tượng để tôi có thể cho phép họ tải lên một biểu mẫu với một trong những hình ảnh trong đó, có cách nào đó sau đó tôi có thể đảo ngược URL đối tượng đó thành một Blobhoặc Filesau đó tôi có thể nối nó vào FormDatavật?


đừng bận tâm về hai bình luận trước - tất cả những gì bạn cần làm là gửi một XMLHttpRequestURL đến blob.
gengkev

2
Tại sao không chỉ đơn giản là lưu trữ các đối tượng tệp gốc ở đâu đó, sau đó sử dụng URL đối tượng để hiển thị chúng và trên biểu mẫu gửi sử dụng các tệp gốc?
dùng764754

@ user764754 vì cố gắng lấy URL của tệp đang được người dùng tải lên hiển thị dưới dạng "C: \ fakepath"
Malcolm Salvador

Câu trả lời:


86

Giải pháp hiện đại:

let blob = await fetch(url).then(r => r.blob());

Url có thể là một url đối tượng hoặc một url bình thường.


3
Đẹp. Vui mừng khi thấy ES5 làm mọi thứ đơn giản như thế này.
BrianFreud

11
Và nếu bạn muốn trực tiếp nhận một tệp từ lời hứa, bạn có thể tạo một tệp như sau. let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })
dbakiu

3
Thật không may, giải pháp này không hiệu quả với tôi trong Chrome. Trình duyệt không tải được URL này.
waldgeist

Waldgeist, bạn đã gói nó trong createdObjectUrl () chưa?
Matt Fletcher

73

Như gengkev ám chỉ trong nhận xét của anh ta ở trên, có vẻ như cách tốt nhất / duy nhất để làm điều này là với một cuộc gọi async xhr2:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:http%3A//your.blob.url.here', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
  if (this.status == 200) {
    var myBlob = this.response;
    // myBlob is now the blob that the object URL pointed to.
  }
};
xhr.send();

Cập nhật (2018): Đối với các tình huống có thể sử dụng ES5 một cách an toàn, Joe có câu trả lời dựa trên ES5 đơn giản hơn bên dưới.


19
Khi nào bạn sẽ có một objectURL không cục bộ với miền và phạm vi hiện tại?
BrianFreud

4
tôi đã làm như trên, nhưng không tìm thấy 404 với xhr. chuyện gì đang xảy ra vậy
albert yu

Xin lưu ý rằng một số trình duyệt (đọc IE cũ ...), nếu bạn cần xử lý những trình duyệt này, hãy xem bài viết stackoverflow.com/questions/17657184/
mẹo

4
@BrianFreud "Khi nào bạn sẽ có một objectURL không cục bộ với miền và phạm vi hiện tại?" Trong trường hợp của tôi, tôi đang cố gắng cung cấp cho Amazon S3 blob một tên tệp khác, đó hiện là "hướng dẫn" trên S3 mà không cần gia hạn. Vì vậy, trong trường hợp của tôi, tôi đang sử dụng một cuộc gọi tên miền chéo.
Wagner Bertolini Junior

6
"URL đối tượng là các URL trỏ đến các tệp trên đĩa." ObjectURL theo định nghĩa chỉ có thể là cục bộ.
BrianFreud

12

Có lẽ ai đó thấy điều này hữu ích khi làm việc với React / Node / Axios. Tôi đã sử dụng tính năng này cho tính năng tải lên hình ảnh Đám mây của mình với react-dropzonetrên Giao diện người dùng.

    axios({
        method: 'get',
        url: file[0].preview, // blob url eg. blob:http://127.0.0.1:8000/e89c5d87-a634-4540-974c-30dc476825cc
        responseType: 'blob'
    }).then(function(response){
         var reader = new FileReader();
         reader.readAsDataURL(response.data); 
         reader.onloadend = function() {
             var base64data = reader.result;
             self.props.onMainImageDrop(base64data)
         }

    })

1
Điều này có làm việc cho các yêu cầu tên miền chéo? Video Twitter có URL blob. Tôi cần có khả năng thu thập đối tượng blob mà URL blob đang trỏ đến. Tôi đang sử dụng tìm nạp api trong trình duyệt, điều này gây ra lỗi này cho tôi - Refused to connect to 'blob:https://twitter.com/9e00aec3-6729-42fb-b5a7-01f50be302fa' because it violates the following Content Security Policy directive: "connect-src . Bạn có thể có một gợi ý những gì tôi có thể làm sai / không nhận được?
gdadsriver

Tôi đã có thể sử dụng một lớp lót này để nhận kết quả với blob dưới dạng thuộc tính dữ liệu: const result = await axios.get (url, {answerType: "blob"});


4

Sử dụng tìm nạp ví dụ như dưới đây:

 fetch(<"yoururl">, {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + <your access token if need>
    },
       })
.then((response) => response.blob())
.then((blob) => {
// 2. Create blob link to download
 const url = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `sample.xlsx`);
 // 3. Append to html page
 document.body.appendChild(link);
 // 4. Force download
 link.click();
 // 5. Clean up and remove the link
 link.parentNode.removeChild(link);
})

Bạn có thể dán vào bảng điều khiển Chrome để kiểm tra. tệp có tải xuống với 'sample.xlsx' Hy vọng nó có thể giúp!


Tôi nghĩ rằng OP đã hỏi về việc chuyển đổi một url blob thành tập tin / blob thực tế, chứ không phải là cách khác.
Abhishek Ghosh

3

Thật không may, câu trả lời của @ BrianFreud không phù hợp với nhu cầu của tôi, tôi có một nhu cầu khác và tôi biết đó không phải là câu trả lời cho câu hỏi của @ BrianFreud, nhưng tôi để nó ở đây vì nhiều người có cùng nhu cầu của tôi. Tôi cần một cái gì đó như 'Làm cách nào để lấy tệp hoặc blob từ một URL?' Và câu trả lời đúng hiện tại không phù hợp với nhu cầu của tôi vì nó không phải là tên miền chéo.

Tôi có một trang web tiêu thụ hình ảnh từ Bộ lưu trữ S3 / Azure của Amazon và ở đó tôi lưu trữ các đối tượng có tên duy nhất:

mẫu: http: //****.blob.core.windows.net/systemimages/bf142dc9-0185-4aee-a3f4-1e5e95a09bcf

Một số hình ảnh này nên được tải xuống từ giao diện hệ thống của chúng tôi. Để tránh truyền lưu lượng này qua máy chủ HTTP của tôi, vì các đối tượng này không yêu cầu bất kỳ bảo mật nào được truy cập (ngoại trừ bằng cách lọc tên miền), tôi đã quyết định thực hiện một yêu cầu trực tiếp trên trình duyệt của người dùng và sử dụng xử lý cục bộ để đặt tên thật cho tệp và sự mở rộng.

Để thực hiện điều đó tôi đã sử dụng bài viết tuyệt vời này từ Henry Algus: http://www.henryalgus.com/reading-binary-files-USE-jquery-ajax/

1. Bước đầu tiên: Thêm hỗ trợ nhị phân cho jquery

/**
*
* jquery.binarytransport.js
*
* @description. jQuery ajax transport for making binary data type requests.
* @version 1.0 
* @author Henry Algus <henryalgus@gmail.com>
*
*/

// use this transport for "binary" data type
$.ajaxTransport("+binary", function (options, originalOptions, jqXHR) {
    // check for conditions and support for blob / arraybuffer response type
    if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob))))) {
        return {
            // create new XMLHttpRequest
            send: function (headers, callback) {
                // setup all variables
                var xhr = new XMLHttpRequest(),
        url = options.url,
        type = options.type,
        async = options.async || true,
        // blob or arraybuffer. Default is blob
        dataType = options.responseType || "blob",
        data = options.data || null,
        username = options.username || null,
        password = options.password || null;

                xhr.addEventListener('load', function () {
                    var data = {};
                    data[options.dataType] = xhr.response;
                    // make callback and send data
                    callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
                });

                xhr.open(type, url, async, username, password);

                // setup custom headers
                for (var i in headers) {
                    xhr.setRequestHeader(i, headers[i]);
                }

                xhr.responseType = dataType;
                xhr.send(data);
            },
            abort: function () {
                jqXHR.abort();
            }
        };
    }
});

2. Bước thứ hai: Thực hiện một yêu cầu sử dụng loại vận chuyển này.

function downloadArt(url)
{
    $.ajax(url, {
        dataType: "binary",
        processData: false
    }).done(function (data) {
        // just my logic to name/create files
        var filename = url.substr(url.lastIndexOf('/') + 1) + '.png';
        var blob = new Blob([data], { type: 'image/png' });

        saveAs(blob, filename);
    });
}

Bây giờ bạn có thể sử dụng Blob được tạo như bạn muốn, trong trường hợp của tôi, tôi muốn lưu nó vào đĩa.

3. Tùy chọn: Lưu tệp trên máy tính của người dùng bằng FileSaver

Tôi đã sử dụng FileSaver.js để lưu vào đĩa đã tải xuống, nếu bạn cần thực hiện điều đó, vui lòng sử dụng thư viện javascript này:

https://github.com/eligrey/FileSaver.js/

Tôi hy vọng điều này sẽ giúp những người khác có nhu cầu cụ thể hơn.


1
ObjectURL là theo định nghĩa địa phương. URL đối tượng là các URL trỏ đến các tệp trên đĩa. Cho rằng không có thứ gọi là objectURL tên miền chéo, bạn đang kết hợp hai khái niệm khác nhau, do đó, vấn đề của bạn là sử dụng giải pháp đã cho.
BrianFreud

1
@BrianFreud Tôi hiểu rằng đó không phải là câu trả lời chính xác cho câu hỏi này. Nhưng tôi vẫn nghĩ rằng đó là một câu trả lời tốt để rời đi kể từ khi tôi đến đây để tìm câu trả lời cho một câu hỏi nhỏ khác nhau 'Làm thế nào để có được một tập tin hoặc blob từ một URL?'. Nếu bạn kiểm tra câu trả lời của chính mình, có 10 câu hỏi về 'Nó không hoạt động trong trường hợp yêu cầu tên miền chéo. '. Vì vậy, hơn 10 người đã đến đây để tìm kiếm điều đó. Tôi quyết định sau đó để nó ở đây.
Wagner Bertolini Junior

Vấn đề là, câu trả lời của bạn không trả lời câu hỏi này. Một URL bình thường và một URL đối tượng là hai thứ hoàn toàn khác nhau. Về # upvote về "Nó không hoạt động trong trường hợp yêu cầu tên miền chéo.", Bạn sẽ không nghĩ giải thích rõ hơn tại sao điều đó không thể thực sự ở đây, và chỉ ra một nơi nào đó hiển thị câu trả lời cho các URL thông thường, thay vào đó hơn những điều khó hiểu ở đây bằng cách kết hợp các URL thông thường và URL đối tượng?
BrianFreud

@BrianFreud vui lòng đề xuất chỉnh sửa câu trả lời của tôi. Tôi sẽ nghiên cứu về chủ đề này, có thể tôi đã hiểu nhầm "objectURL" là gì so với "URL đối tượng" ... Tiếng Anh không phải là tiếng mẹ đẻ của tôi. Tôi sẽ thực hiện một nghiên cứu về chủ đề này để đưa ra một câu trả lời tốt hơn. Nhưng tôi nghĩ rằng đó là những người khác đến đây để tìm kiếm một cái gì đó khác biệt. Tôi đã không tìm kiếm "objectURL" để đến đây, đó là quan điểm của tôi trước đây. Nhưng tôi cũng có bạn.
Wagner Bertolini Junior

2

Nếu bạn vẫn hiển thị tệp trong một canvas, bạn cũng có thể chuyển đổi nội dung canvas thành một đối tượng blob.

canvas.toBlob(function(my_file){
  //.toBlob is only implemented in > FF18 but there is a polyfill 
  //for other browsers https://github.com/blueimp/JavaScript-Canvas-to-Blob
  var myBlob = (my_file);
})

2
Lưu ý rằng điều đó không thực sự mang lại cho bạn cùng một tệp gốc; nó đang tạo một tập tin hình ảnh mới một cách nhanh chóng. Khi tôi thử nghiệm lần cuối cách đây vài năm, tôi thấy rằng ít nhất trong Chrome, tệp hình ảnh mới hoàn toàn không bị nén - tôi đã có 10 nghìn jpg chuyển thành 2,5 mb jpg.
BrianFreud
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.