É possível gravar dados em arquivos usando apenas JavaScript?

192

Quero gravar dados em um arquivo existente usando JavaScript. Não quero imprimi-lo no console. Quero realmente gravar dados para abc.txt. Eu li muitas perguntas respondidas, mas em todos os lugares onde elas estão imprimindo no console. em algum lugar eles deram código, mas não está funcionando. Então, por favor, alguém pode me ajudar Como realmente gravar dados em arquivo.

Consultei o código, mas não está funcionando: está dando erro:

Uncaught TypeError: Construtor ilegal

no cromo e

SecurityError: a operação é insegura.

no Mozilla

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

Então, podemos realmente gravar dados em um arquivo usando apenas Javascript ou NÃO?

pareshm
fonte
2
Verifique isto irmão: stackoverflow.com/questions/585234/…
welbornio

Respostas:

90

Algumas sugestões para isso -

  1. Se você estiver tentando gravar um arquivo na máquina cliente, não poderá fazer isso de nenhuma maneira no navegador. O IE possui métodos para permitir que aplicativos "confiáveis" usem objetos ActiveX para ler / gravar arquivos.
  2. Se você estiver tentando salvá-lo no servidor, basta passar os dados de texto para o servidor e executar o código de gravação de arquivo usando algum idioma do servidor.
  3. Para armazenar algumas informações consideravelmente pequenas no cliente, você pode usar cookies.
  4. Usando a API HTML5 para armazenamento local.
Sujit Agarwal
fonte
27
A API HTML5 possui no máximo 5 MB.
Pacerier 26/06
Sim, você não pode gravar no arquivo do sistema sem selecioná-lo. Leia Oficial Docs: w3.org/TR/file-upload/#security-discussion
Manoj Ghediya
217

Você pode criar arquivos no navegador usando Blobe URL.createObjectURL. Todos os navegadores recentes suportam isso .

Você não pode salvar diretamente o arquivo criado, pois isso causaria grandes problemas de segurança, mas você pode fornecê-lo como um link de download para o usuário. Você pode sugerir um nome de arquivo por meio do downloadatributo do link, em navegadores que suportam o atributo de download. Como em qualquer outro download, o usuário que baixar o arquivo terá a palavra final no nome do arquivo.

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

Aqui está um exemplo que usa essa técnica para salvar texto arbitrário de a textarea.

Se você deseja iniciar imediatamente o download em vez de exigir que o usuário clique em um link, pode usar eventos do mouse para simular um clique do mouse no link, como fez a resposta do Lifecube . Eu criei um exemplo atualizado que usa essa técnica.

  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);
Código Inútil
fonte
1
@FirstBlood Que parte não está funcionando, você está recebendo um erro? A criação de arquivos e links deve funcionar no Safari 7+ (acredito que essas coisas também devem funcionar no Safari 6 se você usar a versão prefixada URL). A configuração do nome do arquivo não funcionará no Safari porque ainda não implementou o downloadatributo .
Código inútil
1
Eu estava tentando no Safari 5.1 :)
First Blood
1
o caractere de nova linha está faltando a partir do doc salvo
Benny
1
@ Benny, os caracteres da nova linha estão lá. JS usa o caractere de nova linha \npara representar novas linhas, como os programas UNIX. Você provavelmente o está visualizando em um programa do Windows, como o Bloco de Notas, que não processa o \ncaractere como uma nova linha . Se você deseja que as novas linhas sejam renderizadas corretamente no Bloco de notas e em alguns outros programas do Windows, antes de colocar o texto na Blobsubstituição, substitua cada um \npor \r\n:text = text.replace(/\n/g, '\r\n') .
Código inútil
2
@ user3241111 Na verdade, não deve funcionar. Coisas assim não são tão incomuns. Eu já vi maneiras hackers de fazer isso ;-) No passado, eu também consegui apenas gerar o arquivo no mouseoverlink, mas dependendo de quanto processamento ele está fazendo e que pode não funcionar muito bem.
Código Inútil
41

Se você estiver falando sobre javascript do navegador, não poderá gravar dados diretamente no arquivo local por motivos de segurança. A nova API do HTML 5 pode permitir apenas a leitura de arquivos.

Mas se você deseja gravar dados, e permitir que o usuário faça o download como um arquivo para o local. o seguinte código funciona:

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

para usá-lo:

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

Lifecube
fonte
Incrível, Lifecube. Eu realmente precisava dessa funcionalidade, embora não queira que o usuário saiba que algum arquivo está sendo baixado, quero que ele fique totalmente oculto, pois pode assustar o usuário ao ver que algum arquivo está sendo baixado automaticamente ao executar alguma ação em um site, embora este seja utilizado apenas para fins de coleta de dados de marketing, você poderia compartilhar como fazer o download do arquivo sem torná-lo visível para o usuário?
Just_another_developer
1
a solução acima está meio desatualizada. Pode ser necessário considerar a lib javascript html 5. github.com/eligrey/FileSaver.js
Lifecube 15/16
@Lifecube usando o FileSaver.js, existe uma maneira de salvar automaticamente o texto em um arquivo sem a interação do usuário? Obrigado! Novo no JS; toda a sua ajuda é apreciada
Nathan
3
Para várias perguntas sobre como salvar um arquivo sem que o usuário saiba: Esse comportamento é exatamente o que é evitado pelo design. Isso abriria a caixa de Pandora de ameaças à segurança fáceis de usar. Os cookies são para coletar dados para fins de marketing.
Ari Okkonen
Nota: Não consigo fazer o download de um arquivo html como .html no firefox v76 no Windows 10. O download tem .pdf anexado ao final do mesmo.
CSchwarz
23

A resposta acima é útil, mas encontrei o código que ajuda você a baixar o arquivo de texto diretamente no clique do botão. Nesse código, você também pode alterar filenamecomo desejar. É pura função javascript com HTML5. Funciona para mim!

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();
}
Niraj
fonte
2
Excelente. Funciona para mim no Opera. Exceto a necessidade de substituir a função desconhecida: "destroyClickedElement" pela instrução "document.body.removeChild (event.target)"
steveOw
3
Você precisa ter cuidado ao usar createObjectURL. Diferentemente da maioria das coisas em JS, os objetos que você cria com ela não são automaticamente coletados como lixo quando não há mais referências a eles; eles são apenas lixo coletado quando a página é fechada. Como você não está usando URL.revokeObjectURL()este código para liberar a memória usada pela última chamada, há um vazamento de memória; se o usuário ligar saveTextFilevárias vezes, continuará consumindo mais e mais memória porque você nunca a liberou.
Código inútil
20

Experimentar

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

Kamil Kiełczewski
fonte
6

No caso de não ser possível usar a nova Blobsolução, que é com certeza a melhor solução no navegador moderno, ainda é possível usar essa abordagem mais simples, que tem um limite no tamanho do arquivo a propósito:

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>

Loretoparisi
fonte
3

Use o código do usuário @ useless-code acima ( https://stackoverflow.com/a/21016088/327386 ) para gerar o arquivo. Se você deseja baixar o arquivo automaticamente, passe o textFileque acabou de ser gerado para esta função:

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;
}
RPM
fonte
5
Não sei por que isso teve um voto negativo. Funciona para mim. As pessoas que votam menos devem deixar um comentário sobre o motivo pelo qual foi votado!
RPM
5
Não diminuí a votação, mas comentar sobre a votação é diretamente desencorajado. O usuário deve comentar o conteúdo de uma postagem e votar no conteúdo de uma postagem, mas não deve comentar seus votos. Se alguém votar sem comentar, você pode entender "esta resposta é útil" ou "esta resposta não é útil", dependendo da votação.
Isso não está funcionando .. Ele não baixa o arquivo. Ele apenas cria um iframe oculto. Eu testei no chrome e no firefox
NaiveCoder 17/08
2

Encontrei boas respostas aqui, mas também encontrei uma maneira mais simples.

O botão para criar o blob e o link de download pode ser combinado em um link, pois o elemento link pode ter um atributo onclick. (O inverso parece não ser possível, adicionar um href a um botão não funciona.)

Você pode estilizar o link como um botão usando bootstrap , que ainda é javascript puro, exceto pelo estilo.

A combinação do botão e do link de download também reduz o código, pois menos desses são feios getElementById são necessárias chamadas .

Este exemplo precisa de apenas um botão para criar o blob de texto e fazer o download:

<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>
Roland
fonte
0

Sim, é possível Aqui o código é

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

SM Turag
fonte