Czy można zapisywać dane do pliku używając tylko JavaScript?


206

Chcę zapisać dane do istniejącego pliku za pomocą JavaScript. Nie chcę drukować tego na konsoli. Chcę faktycznie zapisywać dane do abc.txt. Czytałem wiele odpowiedzi na pytania, ale wszędzie tam, gdzie drukują na konsoli. w którymś miejscu podali kod, ale nie działa. Więc proszę, czy ktoś może mi pomóc Jak właściwie zapisywać dane do pliku.

Odniosłem się do kodu, ale nie działa: podaje błąd:

Uncaught TypeError: Illegal constructor

na chrome i

SecurityError: Operacja jest niezabezpieczona.

w Mozilli

var f = "sometextfile.txt";

writeTextFile(f, "Spoon")
writeTextFile(f, "Cheese monkey")
writeTextFile(f, "Onion")

function writeTextFile(afilename, output)
{
  var txtFile =new File(afilename);
  txtFile.writeln(output);
  txtFile.close();
}

Czy możemy więc zapisywać dane do pliku, używając tylko JavaScript, czy NIE?



Odpowiedzi:


95

Kilka sugestii na ten temat -

  1. Jeśli próbujesz napisać plik na komputerze klienta, nie możesz tego zrobić w żaden sposób w różnych przeglądarkach. IE ma metody umożliwiające „zaufanym” aplikacjom używanie obiektów ActiveX do odczytu / zapisu plików.
  2. Jeśli próbujesz zapisać go na swoim serwerze, po prostu prześlij dane tekstowe na swój serwer i wykonaj plik zapisujący kod w jakimś języku po stronie serwera.
  3. Aby przechowywać niektóre informacje po stronie klienta, które są dość małe, możesz przejść do plików cookie.
  4. Korzystanie z interfejsu API HTML5 do przechowywania lokalnego.

28
Interfejs API HTML5 ma maksymalny rozmiar tylko 5 MB.
Pacerier

Tak, nie możesz pisać do pliku systemowego bez jego zaznaczenia. Przeczytaj oficjalne dokumenty: w3.org/TR/file-upload/#security-discussion
Manoj Ghediya

224

Możesz tworzyć pliki w przeglądarce za pomocą Blobi URL.createObjectURL. Obsługują to wszystkie najnowsze przeglądarki .

Nie możesz bezpośrednio zapisać utworzonego pliku, ponieważ spowodowałoby to ogromne problemy z bezpieczeństwem, ale możesz udostępnić go jako łącze do pobrania dla użytkownika. W przeglądarkach obsługujących atrybut pobierania możesz zasugerować nazwę pliku za pomocą downloadatrybutu łącza. Podobnie jak w przypadku każdego innego pobierania, użytkownik pobierający plik będzie miał ostatnie słowo w sprawie nazwy pliku.

var textFile = null,
  makeTextFile = function (text) {
    var data = new Blob([text], {type: 'text/plain'});

    // If we are replacing a previously generated file we need to
    // manually revoke the object URL to avoid memory leaks.
    if (textFile !== null) {
      window.URL.revokeObjectURL(textFile);
    }

    textFile = window.URL.createObjectURL(data);

    // returns a URL you can use as a href
    return textFile;
  };

Oto przykład, w którym zastosowano tę technikę, aby zapisać dowolny tekst z pliku textarea.

Jeśli chcesz natychmiast rozpocząć pobieranie zamiast wymagać od użytkownika kliknięcia łącza, możesz użyć zdarzeń myszy, aby zasymulować kliknięcie linku myszą, tak jak zrobiła to odpowiedź Lifecube . Stworzyłem zaktualizowany przykład wykorzystujący tę technikę.

  var create = document.getElementById('create'),
    textbox = document.getElementById('textbox');

  create.addEventListener('click', function () {
    var link = document.createElement('a');
    link.setAttribute('download', 'info.txt');
    link.href = makeTextFile(textbox.value);
    document.body.appendChild(link);

    // wait for the link to be added to the document
    window.requestAnimationFrame(function () {
      var event = new MouseEvent('click');
      link.dispatchEvent(event);
      document.body.removeChild(link);
    });

  }, false);

1
@FirstBlood Która część nie działa, czy pojawia się błąd? Tworzenie plików i łączy powinno działać w Safari 7+ (uważam, że rzeczy powinny działać również w Safari 6, jeśli używasz wersji z prefiksem URL). Ustawienie nazwy pliku nie zadziała w Safari, ponieważ nadal nie ma zaimplementowanego downloadatrybutu .
Bezużyteczny kod

1
Próbowałem na Safari 5.1 :)
First Blood

1
w zapisanym dokumencie brakuje znaku nowej linii
Benny

1
@Benny są tam znaki nowego wiersza. JS używa znaku nowej linii \ndo reprezentowania nowych linii, tak jak robią to programy UNIX. Prawdopodobnie przeglądasz go w programie Windows, takim jak Notatnik, który nie renderuje \nznaku jako nowej linii . Jeśli chcesz newlines być poprawnie renderowane w Notatniku i niektórych innych programów Windows, przed oddaniem tekstu do Blobzastąpienia każdego \nz \r\n: text = text.replace(/\n/g, '\r\n').
Bezużyteczny kod

2
@ user3241111 Nie bardzo, to powinno działać. Takie rzeczy nie są wcale takie niezwykłe. Widziałem bardziej hakerskie sposoby na zrobienie tego ;-) W przeszłości również udało mi się po prostu wygenerować plik mouseoverprzez łącze, ale w zależności od tego, ile przetwarza, może to nie działać dobrze.
Bezużyteczny kod

42

Jeśli mówisz o javascript w przeglądarce, ze względów bezpieczeństwa nie możesz zapisywać danych bezpośrednio do pliku lokalnego. Nowy interfejs API HTML 5 umożliwia tylko odczytywanie plików.

Ale jeśli chcesz zapisać dane i umożliwić użytkownikowi pobieranie jako plik do lokalnego. następujący kod działa:

    function download(strData, strFileName, strMimeType) {
    var D = document,
        A = arguments,
        a = D.createElement("a"),
        d = A[0],
        n = A[1],
        t = A[2] || "text/plain";

    //build download link:
    a.href = "data:" + strMimeType + "charset=utf-8," + escape(strData);


    if (window.MSBlobBuilder) { // IE10
        var bb = new MSBlobBuilder();
        bb.append(strData);
        return navigator.msSaveBlob(bb, strFileName);
    } /* end if(window.MSBlobBuilder) */



    if ('download' in a) { //FF20, CH19
        a.setAttribute("download", n);
        a.innerHTML = "downloading...";
        D.body.appendChild(a);
        setTimeout(function() {
            var e = D.createEvent("MouseEvents");
            e.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            a.dispatchEvent(e);
            D.body.removeChild(a);
        }, 66);
        return true;
    }; /* end if('download' in a) */



    //do iframe dataURL download: (older W3)
    var f = D.createElement("iframe");
    D.body.appendChild(f);
    f.src = "data:" + (A[2] ? A[2] : "application/octet-stream") + (window.btoa ? ";base64" : "") + "," + (window.btoa ? window.btoa : escape)(strData);
    setTimeout(function() {
        D.body.removeChild(f);
    }, 333);
    return true;
}

używać go:

download('the content of the file', 'filename.txt', 'text/plain');


Niesamowite, Lifecube. Naprawdę potrzebowałem tej funkcji, chociaż nie chcę, aby użytkownik wiedział, że jakikolwiek plik jest pobierany, chcę, aby był całkowicie ukryty przed użytkownikiem, ponieważ może to wystraszyć użytkownika, gdy zobaczy, że jakiś plik jest pobierany automatycznie po wykonaniu jakiejś czynności na stronie internetowej, chociaż używamy jej wyłącznie do zbierania danych marketingowych, czy możesz podać, jak pobrać plik bez udostępniania go użytkownikowi?
Just_another_developer

1
powyższe rozwiązanie jest trochę przestarzałe. Konieczne może być rozważenie HTML 5 javascript lib. github.com/eligrey/FileSaver.js
Lifecube

@Lifecube za pomocą FileSaver.js, czy istnieje sposób na automatyczne zapisywanie tekstu do pliku bez interakcji użytkownika? Dzięki! Nowy w JS; cała twoja pomoc jest doceniana
Nathan

3
Do kilku pytań dotyczących zapisywania pliku bez wiedzy użytkownika: Projekt unika takiego zachowania. To otworzyłoby puszkę Pandory z łatwymi w użyciu zagrożeniami bezpieczeństwa. Pliki cookie służą do zbierania danych w celach marketingowych.
Ari Okkonen

Uwaga Nie mogę pobrać tego pliku, aby pobrać plik html jako .html w przeglądarce Firefox v76 w systemie Windows 10. Pobieranie ma rozszerzenie .pdf na końcu.
CSchwarz

23

Powyższa odpowiedź jest przydatna, ale znalazłem kod, który pomaga pobrać plik tekstowy bezpośrednio po kliknięciu przycisku. W tym kodzie możesz również zmienić, filenamejak chcesz. To czysta funkcja javascript z HTML5. Pracuje dla mnie!

function saveTextAsFile()
{
    var textToWrite = document.getElementById("inputTextToSave").value;
    var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
    var fileNameToSaveAs = document.getElementById("inputFileNameToSaveAs").value;
      var downloadLink = document.createElement("a");
    downloadLink.download = fileNameToSaveAs;
    downloadLink.innerHTML = "Download File";
    if (window.webkitURL != null)
    {
        // Chrome allows the link to be clicked
        // without actually adding it to the DOM.
        downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
    }
    else
    {
        // Firefox requires the link to be added to the DOM
        // before it can be clicked.
        downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
        downloadLink.onclick = destroyClickedElement;
        downloadLink.style.display = "none";
        document.body.appendChild(downloadLink);
    }

    downloadLink.click();
}

2
Świetny. U mnie działa w Operze. Z wyjątkiem konieczności zastąpienia nieznanej funkcji: "zniszczClickedElement" instrukcją "document.body.removeChild (event.target)"
steveOw

3
Należy zachować ostrożność podczas używania createObjectURL. W przeciwieństwie do większości rzeczy w JS, obiekty, które za jej pomocą tworzysz, nie są automatycznie usuwane jako śmieci, gdy nie ma już do nich odniesień; są one zbierane tylko po zamknięciu strony. Ponieważ URL.revokeObjectURL()w tym kodzie nie używasz do zwolnienia pamięci używanej przez ostatnie wywołanie, masz wyciek pamięci; jeśli użytkownik dzwoni saveTextFilewiele razy, będą nadal zużywać coraz więcej pamięci, ponieważ nigdy jej nie zwolniłeś.
Bezużyteczny kod

23

Próbować

let a = document.createElement('a');
a.href = "data:application/octet-stream,"+encodeURIComponent("My DATA");
a.download = 'abc.txt';
a.click();

Jeśli chcesz pobrać dane binarne, zajrzyj tutaj

Aktualizacja

2020.06.14 Uaktualniam Chrome do 83.0 i nowszych SO snippet stop działa (powód: ograniczenia bezpieczeństwa piaskownicy ) - ale wersja JSFiddle działa - tutaj


7

W przypadku braku możliwości skorzystania z nowego Blobrozwiązania, czyli z pewnością najlepszego rozwiązania w nowoczesnej przeglądarce, nadal można skorzystać z prostszego podejścia, które przy okazji ma ograniczenie rozmiaru pliku:

function download() {
                var fileContents=JSON.stringify(jsonObject, null, 2);
                var fileName= "data.json";

                var pp = document.createElement('a');
                pp.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContents));
                pp.setAttribute('download', fileName);
                pp.click();
            }
            setTimeout(function() {download()}, 500);

$('#download').on("click", function() {
  function download() {
    var jsonObject = {
      "name": "John",
      "age": 31,
      "city": "New York"
    };
    var fileContents = JSON.stringify(jsonObject, null, 2);
    var fileName = "data.json";

    var pp = document.createElement('a');
    pp.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContents));
    pp.setAttribute('download', fileName);
    pp.click();
  }
  setTimeout(function() {
    download()
  }, 500);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="download">Download me</button>


3

Użyj kodu użytkownika @ useless-code powyżej ( https://stackoverflow.com/a/21016088/327386 ), aby wygenerować plik. Jeśli chcesz pobrać plik automatycznie, przekaż textFilewłaśnie wygenerowany plik do tej funkcji:

var downloadFile = function downloadURL(url) {
    var hiddenIFrameID = 'hiddenDownloader',
    iframe = document.getElementById(hiddenIFrameID);
    if (iframe === null) {
        iframe = document.createElement('iframe');
        iframe.id = hiddenIFrameID;
        iframe.style.display = 'none';
        document.body.appendChild(iframe);
    }
    iframe.src = url;
}

5
Nie wiem, dlaczego to dało głos negatywny. Mi to pasuje. Osoby, które głosują negatywnie, powinny przynajmniej zostawić komentarz wyjaśniający, dlaczego głosowanie negatywne zostało odrzucone!
RPM

5
Nie głosowałem przeciw, ale w rzeczywistości komentowanie głosowania jest bezpośrednio odradzane. Użytkownik powinien komentować treść wpisu i głosować nad treścią wpisu, ale nie powinien komentować swoich głosów. Jeśli ktoś zagłosuje bez komentowania, możesz przyjąć, że w zależności od oddanego głosu oznacza to „ta odpowiedź jest przydatna” lub „ta odpowiedź jest nieprzydatna”.

To nie działa. Nie pobiera pliku. Po prostu tworzy element iframe, który jest ukryty. Testowałem na Chrome i Firefox
NaiveCoder

2

Znalazłem tutaj dobre odpowiedzi, ale znalazłem też prostszy sposób.

Przycisk do tworzenia obiektu blob i łącze pobierania można połączyć w jeden link, ponieważ element link może mieć atrybut onclick. (Odwrotna sytuacja wydaje się niemożliwa, dodawanie href do przycisku nie działa.)

Możesz stylizować link jako przycisk za pomocą bootstrap, który nadal jest czystym javascriptem, z wyjątkiem stylizacji.

Połączenie przycisku i linku pobierania również ogranicza kod, ponieważ potrzeba mniej tych brzydkich getElementByIdwywołań.

W tym przykładzie wystarczy jedno kliknięcie przycisku, aby utworzyć tekstowy obiekt blob i pobrać go:

<a id="a_btn_writetofile" download="info.txt" href="#" class="btn btn-primary" 
   onclick="exportFile('This is some dummy data.\nAnd some more dummy data.\n', 'a_btn_writetofile')"
>
   Write To File
</a>

<script>
    // URL pointing to the Blob with the file contents
    var objUrl = null;
    // create the blob with file content, and attach the URL to the downloadlink; 
    // NB: link must have the download attribute
    // this method can go to your library
    function exportFile(fileContent, downloadLinkId) {
        // revoke the old object URL to avoid memory leaks.
        if (objUrl !== null) {
            window.URL.revokeObjectURL(objUrl);
        }
        // create the object that contains the file data and that can be referred to with a URL
        var data = new Blob([fileContent], { type: 'text/plain' });
        objUrl = window.URL.createObjectURL(data);
        // attach the object to the download link (styled as button)
        var downloadLinkButton = document.getElementById(downloadLinkId);
        downloadLinkButton.href = objUrl;
    };
</script>

zablokowane przez kaspersky :(
Lokinou

@Lokinou Security uber alles, ale czy wiesz też, dlaczego jest zablokowany?
Roland

przypuszczam, że uniemożliwiłoby to pobieranie niechcianych plików bez działania użytkownika.
Lokinou

0
const data = {name: 'Ronn', age: 27};              //sample json
const a = document.createElement('a');
const blob = new Blob([JSON.stringify(data)]);
a.href = URL.createObjectURL(blob);
a.download = 'sample-profile';                     //filename to download
a.click();

Sprawdź dokumentację obiektu Blob tutaj - Blob MDN, aby zapewnić dodatkowe parametry dla typu pliku. Domyślnie utworzy plik .txt


-2

Tak, to możliwe. Oto kod

const fs = require('fs') 
let data = "Learning how to write in a file."
fs.writeFile('Output.txt', data, (err) => { 
      
    // In case of a error throw err. 
    if (err) throw err; 
}) 

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.