Nazwa pliku blob JavaScript bez linku

189

Jak ustawić nazwę pliku blob w JavaScript podczas wymuszania pobierania go przez 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);
}

Uruchomienie powyższego kodu powoduje natychmiastowe pobranie pliku bez odświeżania strony, która wygląda następująco:

bfefe410-8d9c-4883-86c5-d76c50a24a1d

Zamiast tego chcę ustawić nazwę pliku jako my-download.json .

Ash Blue
źródło

Odpowiedzi:

312

Jedyny sposób, w jaki jestem świadomy, to sztuczka używana przez FileSaver.js :

  1. Utwórz ukryty <a>tag.
  2. Ustaw jego hrefatrybut na adres URL obiektu blob.
  3. Ustaw jego downloadatrybut na nazwę pliku.
  4. Kliknij <a>znacznik.

Oto uproszczony przykład ( 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);

Napisałem ten przykład, aby zilustrować ten pomysł, zamiast tego w kodzie produkcyjnym użyj FileSaver.js.

Notatki

  • Starsze przeglądarki nie obsługują atrybutu „pobierz”, ponieważ jest to część HTML5.
  • Niektóre formaty plików są uważane przez przeglądarkę za niebezpieczne i pobieranie się nie udaje. Zapisywanie plików JSON z rozszerzeniem txt działa dla mnie.
kol
źródło
2
@AshBlue Atrybut „pobierz” wymaga HTML5. Mój kod jest tylko przykładem, możesz także wypróbować stronę demonstracyjną FileSaver.js: eligrey.com/demos/FileSaver.js
kol.
1
Co ciekawe, jeśli wielokrotnie próbujesz pobrać txt w ten sposób (naciskając wielokrotnie przycisk Uruchom na jsfiddle.net), pobieranie czasami kończy się niepowodzeniem.
kol
2
Chciałem tylko wspomnieć, że to rozwiązanie nie będzie działać dla plików o rozmiarach większych niż określony próg. np.> 2 MB dla chrome. Rozmiar ten różni się w zależności od przeglądarki.
manojadams
3
To nie działa dla mnie, ponieważ muszę otworzyć plik w nowej karcie. Muszę pokazać plik PDF w Chrome, ale muszę pokazać przyjazną nazwę użytkownika na pasku narzędzi URL, a jeśli użytkownik chce pobrać za pomocą ikony pobierania, muszę umieścić tę samą przyjazną nazwę w pliku.
Adrian Paredes,
1
Wystarczy dodać, że nie trzeba tak naprawdę montować tagu na ciele, aby to zadziałało (wypróbowano teraz w Chrome)
poza kodem
52

Chciałem tylko rozwinąć akceptowaną odpowiedź z obsługą Internet Explorera (w każdym razie najnowocześniejszych wersji) i uporządkować kod za pomocą 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();
}

Oto przykład Fiddle . Godspeed .

Alexandru
źródło
Działał idealnie.
N8allan
1
Użyłem zaakceptowanego rozwiązania, ale nie zadziałało w Firefoksie! Nadal nie wiem dlaczego. Twoje rozwiązanie działało w Firefoksie. Dzięki.
elahehab
@elahehab Moje rozwiązania zawsze działają;)
Alexandru
27

Ta sama zasada co powyższe rozwiązania. Ale miałem problemy z Firefoksem 52.0 (32 bity), w którym duże pliki (> 40 MB) są obcinane w losowych pozycjach. Ponowne planowanie wywołania metody revokeObjectUrl () rozwiązuje ten problem.

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

przykład jsfiddle

Kim Nyholm
źródło
1
Odkryłem, że ten hack setTimeout () naprawia MS Edge, w którym plik w ogóle się nie pobierał. Jednak tylko wywołanie revokeObjectURL () musi zostać opóźnione.
Russell Phillips,
Przekonałem się, że „if (window.navigator.msSaveOrOpenBlob)” jest tym, co mi
pomogło
23

Późno, ale ponieważ miałem ten sam problem, dodaję swoje rozwiązanie:

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);
    }
}
ben
źródło
5
Dzięki @ben. To działa dobrze. Brak elementów dom, nic jak wyzwalanie jak zdarzenie kliknięcia. Po prostu działa świetnie z odpowiednim rozszerzeniem. Ale podana nazwa pliku nie jest brana pod uwagę, pobieranie „<object_url_id> .csv” zamiast „<myfileName> .csv”
Ram Babu S
3
Wywołanie revokeObjectURLpo location.assigndziała poprawnie w przeglądarce Firefox, ale przerywa pobieranie w Chrome.
Fred
Uwaga: „Edge nie obsługuje konstruktora plików”. Nr ref. caniuse.com/#feat=fileapi
user1477388
To powinna być poprawna odpowiedź. Nie ma sensu tworzyć bezużytecznych obiektów w drzewie DOM
Luiz Felipe
Teraz tak jest, od stycznia 20-20
Luiz Felipe
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);
        }
    }
Jean-Philippe
źródło
czy jest jakiś sposób, aby otworzyć w nim nowe okno?
Enrique Altuna
Myślę, że możesz zadzwonić link.click()zamiast wywołać zdarzenie myszy.
Fred
2

Przykład roboczy działania przycisku pobierania, aby zapisać zdjęcie kota z adresu URL jako „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();
}
użytkownik1032613
źródło
1

Window.location.assign nie działało dla mnie. pobiera dobrze, ale pobiera bez rozszerzenia pliku CSV na platformie Windows. Poniższe działało dla mnie.

    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);
Sacky San
źródło
0

To jest moje rozwiązanie. Z mojego punktu widzenia nie można ominąć <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>

dabeng
źródło