Имя файла большого двоичного объекта JavaScript без ссылки

203

Как установить имя файла большого двоичного объекта в JavaScript при принудительной его загрузке через window.location?

function newFile(data) {
    var json = JSON.stringify(data);
    var blob = new Blob([json], {type: "octet/stream"});
    var url  = window.URL.createObjectURL(blob);
    window.location.assign(url);
}

При выполнении приведенного выше кода файл мгновенно загружается без обновления страницы, что выглядит так:

bfefe410-8d9c-4883-86c5-d76c50a24a1d

Вместо этого я хочу установить имя файла my-download.json .

Пепельно-синий
источник

Ответы:

332

Единственный способ, о котором я знаю, - это трюк, используемый FileSaver.js :

  1. Создайте скрытый <a>тег.
  2. Установите для его hrefатрибута URL-адрес большого двоичного объекта.
  3. Установите его downloadатрибут на имя файла.
  4. Щелкните по <a>тегу.

Вот упрощенный пример ( jsfiddle ):

var saveData = (function () {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    return function (data, fileName) {
        var json = JSON.stringify(data),
            blob = new Blob([json], {type: "octet/stream"}),
            url = window.URL.createObjectURL(blob);
        a.href = url;
        a.download = fileName;
        a.click();
        window.URL.revokeObjectURL(url);
    };
}());

var data = { x: 42, s: "hello, world", d: new Date() },
    fileName = "my-download.json";

saveData(data, fileName);

Я написал этот пример, чтобы проиллюстрировать идею, вместо этого в производственном коде используйте FileSaver.js.

Примечания

  • Старые браузеры не поддерживают атрибут «загрузка», поскольку он является частью HTML5.
  • Некоторые форматы файлов считаются браузером небезопасными, и загрузка не выполняется. У меня работает сохранение файлов JSON с расширением txt.
кол
источник
2
@AshBlue Атрибут "загрузка" требует HTML5. Мой код - это просто пример, вы также можете попробовать демонстрационную страницу FileSaver.js
kol
1
Интересно, что если вы неоднократно пытаетесь загрузить текстовый файл таким способом (снова и снова нажимая кнопку «Выполнить» на jsfiddle.net), загрузка иногда завершается ошибкой.
коль
2
Просто хотел упомянуть, что это решение не будет работать для файлов, размер которых превышает определенный порог. например -> 2 МБ для хрома. Этот размер варьируется от браузера к браузеру.
manojadams 02
6
У меня это не работает, потому что мне нужно открыть файл в новой вкладке. Мне нужно показать PDF-файл в Chrome, но мне нужно показать удобное имя на панели инструментов URL-адреса, и если пользователь хочет загрузить через значок загрузки, я должен указать такое же удобное имя в файле.
Адриан Паредес,
2
Чтобы добавить, вам не нужно на самом деле монтировать тег a к телу, чтобы это работало (пробовал только что в Chrome)
за пределами кода
55

Я просто хотел расширить принятый ответ за счет поддержки Internet Explorer (в любом случае, большинства современных версий) и привести в порядок код с помощью jQuery:

$(document).ready(function() {
    saveFile("Example.txt", "data:attachment/text", "Hello, world.");
});

function saveFile (name, type, data) {
    if (data !== null && navigator.msSaveBlob)
        return navigator.msSaveBlob(new Blob([data], { type: type }), name);
    var a = $("<a style='display: none;'/>");
    var url = window.URL.createObjectURL(new Blob([data], {type: type}));
    a.attr("href", url);
    a.attr("download", name);
    $("body").append(a);
    a[0].click();
    window.URL.revokeObjectURL(url);
    a.remove();
}

Вот пример Fiddle . Удачи .

Александру
источник
Работает отлично.
N8allan
1
Я использовал принятое решение, но в firefox оно не сработало! Я до сих пор не знаю почему. Ваше решение работало в firefox. Спасибо.
elahehab
31

Тот же принцип, что и в решениях выше. Но у меня были проблемы с Firefox 52.0 (32-разрядная версия), где большие файлы (> 40 МБ) усекались в случайных местах. Повторное планирование вызова revokeObjectUrl () устраняет эту проблему.

function saveFile(blob, filename) {
  if (window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(blob, filename);
  } else {
    const a = document.createElement('a');
    document.body.appendChild(a);
    const url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = filename;
    a.click();
    setTimeout(() => {
      window.URL.revokeObjectURL(url);
      document.body.removeChild(a);
    }, 0)
  }
}

jsfiddle пример

Ким Нихолм
источник
1
Я обнаружил, что этот хак setTimeout () исправляет MS Edge, при котором файл вообще не загружался. Однако нужно отложить только вызов revokeObjectURL ().
Рассел Филлипс
Я обнаружил, что «if (window.navigator.msSaveOrOpenBlob)» помогло мне
Жак Оливье
23

Поздно, но поскольку у меня была такая же проблема, я добавляю свое решение:

function newFile(data, fileName) {
    var json = JSON.stringify(data);
    //IE11 support
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
        let blob = new Blob([json], {type: "application/json"});
        window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else {// other browsers
        let file = new File([json], fileName, {type: "application/json"});
        let exportUrl = URL.createObjectURL(file);
        window.location.assign(exportUrl);
        URL.revokeObjectURL(exportUrl);
    }
}
Бен
источник
5
Спасибо @ben. Это нормально работает. Никаких элементов dom, ничего похожего на событие щелчка. Он просто отлично работает с правильным расширением. Но данное имя файла не учитывается, скачивается "<object_url_id> .csv" вместо "<myfileName> .csv"
Ram Babu S
3
Вызов revokeObjectURLпосле location.assignработает нормально в Firefox, но прерывает загрузку в Chrome.
Фред
Обратите внимание, что «Edge не поддерживает конструктор файлов». Ref. caniuse.com/#feat=fileapi
user1477388
Это должен быть правильный ответ. Нет смысла создавать бесполезные объекты в дереве DOM
Луис Фелипе
Теперь это так, с января '20
Луис Фелипе
6
saveFileOnUserDevice = function(file){ // content: blob, name: string
        if(navigator.msSaveBlob){ // For ie and Edge
            return navigator.msSaveBlob(file.content, file.name);
        }
        else{
            let link = document.createElement('a');
            link.href = window.URL.createObjectURL(file.content);
            link.download = file.name;
            document.body.appendChild(link);
            link.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true, view: window}));
            link.remove();
            window.URL.revokeObjectURL(link.href);
        }
    }
Жан-Филипп
источник
есть ли способ открыть в нем новое окно?
Энрике Алтуна
Я думаю, вы можете вызвать link.click()вместо отправки события мыши.
Фред
3

Рабочий пример кнопки загрузки для сохранения фотографии кошки с адреса как «cat.jpg»:

HTML:

<button onclick="downloadUrl('https://i.imgur.com/AD3MbBi.jpg', 'cat.jpg')">Download</button>

JavaScript:

function downloadUrl(url, filename) {
  let xhr = new XMLHttpRequest();
  xhr.open("GET", url, true);
  xhr.responseType = "blob";
  xhr.onload = function(e) {
    if (this.status == 200) {
      const blob = this.response;
      const a = document.createElement("a");
      document.body.appendChild(a);
      const blobUrl = window.URL.createObjectURL(blob);
      a.href = blobUrl;
      a.download = filename;
      a.click();
      setTimeout(() => {
        window.URL.revokeObjectURL(blobUrl);
        document.body.removeChild(a);
      }, 0);
    }
  };
  xhr.send();
}
user1032613
источник
1

window.location.assign у меня не работал. он загружается нормально, но загружается без расширения для файла CSV на платформе Windows. Следующее сработало для меня.

    var blob = new Blob([csvString], { type: 'text/csv' });
    //window.location.assign(window.URL.createObjectURL(blob));
    var link = window.document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    // Construct filename dynamically and set to link.download
    link.download = link.href.split('/').pop() + '.' + extension; 
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
Саки Сан
источник
0

Это мое решение. С моей точки зрения, вы не можете обойти <a>.

function export2json() {
  const data = {
    a: '111',
    b: '222',
    c: '333'
  };
  const a = document.createElement("a");
  a.href = URL.createObjectURL(
    new Blob([JSON.stringify(data, null, 2)], {
      type: "application/json"
    })
  );
  a.setAttribute("download", "data.json");
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}
<button onclick="export2json()">Export data to json file</button>

дабенг
источник