Как получить файл или блоб из объекта URL?

Я разрешаю пользователю загружать изображения на страницу с помощью перетаскивания и других методов. Когда изображение сброшено, я использую URL.createObjectURL преобразовать в URL объекта для отображения изображения. Я не отменяю URL, как я его повторно использую.

Итак, когда приходит время создавать FormData объект, чтобы я мог разрешить им загружать форму с одним из этих изображений, есть ли какой-то способ, которым я могу затем повернуть этот URL объекта обратно в Blob или же File так что я могу затем добавить его к FormData объект?

9 ответов

Решение

Как упоминает gengkev в своем комментарии выше, похоже, что лучший / единственный способ сделать это - вызов 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();

Обновление (2018): В ситуациях, когда можно безопасно использовать ES5, у Джо есть более простой ответ на основе ES5 ниже.

Современное решение:

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

URL может быть URL объекта или обычным URL.

Возможно, кто-то найдет это полезным при работе с React/Node/Axios. Я использовал это для своей функции загрузки облачных изображений с react-dropzone на пользовательском интерфейсе.

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

    })

Проблема с получением URL-адреса большого двоичного объекта снова заключается в том, что это создаст копию данных большого двоичного объекта. Это может довольно быстро снизить использование памяти.

Очень жаль, что File API не дает нам доступа к связанным в настоящее время BLOB-объектам, определенно они думали, что веб-авторы должны сами хранить этот Blob-объект во время создания, и это правда.

Но для тех, кто не несет ответственности за создание blob:// URI (например, потому, что это сделала библиотека), мы все равно можем заполнить эту дыру API самостоятельно.

И еще одно преимущество состоит в том, что он может даже извлекать объекты MediaSource , в то время как решения для извлечения в этом случае просто ошиблись бы.

Используя выборку, например, как показано ниже:

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

Вы можете вставить в консоль Chrome для тестирования. файл с загрузкой с 'sample.xlsx' Надеюсь, это поможет!

См. Получение данных BLOB из запроса XHR, в котором указано, что BlobBuilder не работает в Chrome, поэтому вам необходимо использовать:

xhr.responseType = 'arraybuffer';

К сожалению, ответ @BrianFreud не соответствует моим потребностям, у меня была немного другая потребность, и я знаю, что это не ответ на вопрос @BrianFreud, но я оставляю его здесь, потому что многие люди попали сюда с моей такой же потребностью. Мне нужно что-то вроде "Как получить файл или блоб из URL?", И текущий правильный ответ не соответствует моим потребностям, потому что он не междоменный.

У меня есть веб-сайт, который использует изображения из хранилища Amazon S3/Azure, и там я храню объекты с уникальными идентификаторами:

Пример: http://****.blob.core.windows.net/systemimages/bf142dc9-0185-4aee-a3f4-1e5e95a09bcf

Некоторые из этих изображений должны быть загружены из интерфейса нашей системы. Чтобы не пропускать этот трафик через мой HTTP-сервер, поскольку для этих объектов не требуется никакой безопасности (кроме фильтрации доменов), я решил сделать прямой запрос в браузере пользователя и использовать локальную обработку, чтобы дать файлу реальное имя и расширение.

Для этого я использовал эту замечательную статью от Генри Альгуса: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/

1. Первый шаг: добавить бинарную поддержку в 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. Второй шаг: сделайте запрос, используя этот тип транспорта.

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

Теперь вы можете использовать BLOB-объект, созданный так, как вы хотите, в моем случае я хочу сохранить его на диск.

3. Необязательно: сохраните файл на компьютере пользователя с помощью FileSaver

Я использовал FileSaver.js для сохранения на диск загруженного файла, если вам нужно это сделать, пожалуйста, используйте эту библиотеку JavaScript:

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

Я ожидаю, что это поможет другим с более конкретными потребностями.

Если вы все равно покажете файл на холсте, вы также можете преобразовать содержимое холста в объект 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);
})

После ответа @Kaiido еще один способ перегрузить URL-адрес, не вмешиваясь в URL-адрес, - это расширить класс URL-адреса следующим образом:

      export class URLwithStore extends URL {
  static createObjectURL(blob) {
    const url = super.createObjectURL(blob);
    URLwithStore.store = { ...(URLwithStore.store ?? {}), [url]: blob };
    return url;
  }

  static getFromObjectURL(url) {
    return (URLwithStore.store ?? {})[url] ?? null;
  }

  static revokeObjectURL(url) {
    super.revokeObjectURL(url);
    if (
      new URL(url).protocol === "blob:" &&
      URLwithStore.store &&
      url in URLwithStore.store
    )
      delete URLwithStore.store[url];
  }
}

использование

      const blob = new Blob( ["foo"] );
const url = URLwithStore.createObjectURL( blob );
const retrieved = URLwithStore.getFromObjectURL( url );
console.log( "retrieved Blob is Same Object?", retrieved === blob );
Другие вопросы по тегам