Использование HTML5/JavaScript для генерации и сохранения файла

В последнее время я возился с WebGL и заставил работать читателя Collada. Проблема в том, что он довольно медленный (Collada - очень многословный формат), поэтому я собираюсь начать преобразование файлов в более простой в использовании формат (вероятно, JSON). У меня уже есть код для синтаксического анализа файла в JavaScript, так что я могу также использовать его как мой экспортер! Проблема в сохранении.

Теперь я знаю, что могу проанализировать файл, отправить результат на сервер и сделать так, чтобы браузер запросил файл с сервера для загрузки. Но на самом деле сервер не имеет ничего общего с этим конкретным процессом, так зачем его привлекать? У меня уже есть содержимое нужного файла в памяти. Можно ли как-то представить пользователю загрузку с использованием чистого JavaScript? (Я сомневаюсь в этом, но с таким же успехом могу спросить...)

И чтобы быть ясным: я не пытаюсь получить доступ к файловой системе без ведома пользователей! Пользователь предоставит файл (возможно, с помощью перетаскивания), скрипт преобразует файл в памяти, и пользователю будет предложено загрузить результат. Все это должно быть "безопасной" деятельностью, если речь идет о браузере.

[РЕДАКТИРОВАТЬ]: Я не упомянул об этом заранее, поэтому постеры, ответившие "Flash", достаточно действительны, но часть того, что я делаю, - это попытка подчеркнуть, что можно сделать с чистым HTML5... так что Flash прямо в моем случае. (Хотя это совершенно правильный ответ для любого, кто делает "настоящее" веб-приложение.) В таком случае мне кажется, что мне не повезло, если я не хочу привлекать сервер. Спасибо, в любом случае!

Ответ 1

ОК, создавая данные: URI определенно делает трюк для меня, благодаря Мэтью и Деннкстеру, указывающим на этот вариант! Вот в основном, как я это делаю:

1) получить все содержимое в строку под названием "контент" (например, создав ее там изначально или путем чтения innerHTML тега уже построенной страницы).

2) Создайте URI данных:

uriContent = "data:application/octet-stream," + encodeURIComponent(content);

Будут ограничения длины в зависимости от типа браузера и т.д., но, например, Firefox 3.6.12 работает до 256K. Кодирование в Base64 вместо использования encodeURIComponent может сделать вещи более эффективными, но для меня это нормально.

3) откройте новое окно и переадресовывайте его в этот URI, чтобы указать местоположение загрузки моей страницы, сгенерированной JavaScript:

newWindow = window.open(uriContent, 'neuesDokument');

Что это.

Ответ 2

Простое решение для готовых браузеров HTML5...

function download(filename, text) {
    var pom = document.createElement('a');
    pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
    pom.setAttribute('download', filename);

    if (document.createEvent) {
        var event = document.createEvent('MouseEvents');
        event.initEvent('click', true, true);
        pom.dispatchEvent(event);
    }
    else {
        pom.click();
    }
}

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

download('test.txt', 'Hello world!');

Ответ 3

HTML5 определил метод window.saveAs(blob, filename). Он не поддерживается ни одним браузером прямо сейчас. Но есть библиотека совместимости, называемая FileSaver.js, которая добавляет эту функцию в большинство современных браузеров (включая Internet Explorer 10+). Internet Explorer 10 поддерживает метод navigator.msSaveBlob(blob, filename) (MSDN), который используется в FileSaver.js для поддержки Internet Explorer.

Я написал сообщение с более подробной информацией об этой проблеме.

Ответ 4

Сохранение больших файлов

Длинные URI данных могут приводить к проблемам с производительностью в браузерах. Другой вариант сохранения созданных на стороне клиента файлов состоит в том, чтобы поместить их содержимое в объект Blob (или File) и создать ссылку для загрузки, используя URL.createObjectURL(blob). Это возвращает URL-адрес, который можно использовать для извлечения содержимого блоба. URL.revokeObjectURL() хранится внутри браузера, пока не будет URL.revokeObjectURL() в URL-адресе, или созданный документ закрыт. Большинство веб-браузеров поддерживают URL-адреса объектов, Opera Mini - единственная, которая их не поддерживает.

Принуждение загрузки

Если данные представляют собой текст или изображение, браузер может открыть файл, а не сохранять его на диск. Чтобы заставить файл загружаться при нажатии ссылки, вы можете использовать атрибут download. Однако не все веб-браузеры поддерживают атрибут загрузки. Другой вариант - использовать application/octet-stream в качестве файла mime-type, но это приводит к тому, что файл представляется как двоичный blob, который особенно непригоден для пользователя, если вы не укажете или не можете указать имя файла. См. Также " Сила, чтобы открыть" Сохранить как... "всплывающее окно в текстовой ссылке нажмите для pdf в HTML ".

Указание имени файла

Если blob создается с помощью конструктора файлов, вы также можете указать имя файла, но только для нескольких браузеров (включая Chrome и Firefox) есть поддержка конструктора файлов. Имя файла также может быть указано как аргумент атрибута download, но это зависит от тонны соображений безопасности. Internet Explorer 10 и 11 предоставляет свой собственный метод msSaveBlob, чтобы указать имя файла.

Пример кода

var file;
var data = [];
data.push("This is a test\n");
data.push("Of creating a file\n");
data.push("In a browser\n");
var properties = {type: 'text/plain'}; // Specify the file mime-type.
try {
  // Specify the filename using the File constructor, but ...
  file = new File(data, "file.txt", properties);
} catch (e) {
  // ... fall back to the Blob constructor if that isn't supported.
  file = new Blob(data, properties);
}
var url = URL.createObjectURL(file);
document.getElementById('link').href = url;
<a id="link" target="_blank" download="file.txt">Download</a>

Ответ 5

function download(content, filename, contentType)
{
    if(!contentType) contentType = 'application/octet-stream';
        var a = document.createElement('a');
        var blob = new Blob([content], {'type':contentType});
        a.href = window.URL.createObjectURL(blob);
        a.download = filename;
        a.click();
}

Ответ 6

Взгляните на Doug Neiner Downloadify, который является интерфейсом JavaScript на основе Flash для этого.

Downloadify - это небольшая библиотека JavaScript + Flash, которая позволяет генерировать и сохранять файлы "на лету" в браузере без взаимодействия с сервером.

Ответ 7

Простое решение!

<a download="My-FileName.txt" href="data:application/octet-stream,HELLO-WORLDDDDDDDD">Click here</a>

Ответ 8

Вы можете сгенерировать URI данных. Однако существуют ограничения, зависящие от браузера.

Ответ 9

Я использовал FileSaver (https://github.com/eligrey/FileSaver.js), и он отлично работает.
Например, я сделал эту функцию для экспорта журналов, отображаемых на странице.
Вы должны передать массив для instanciation Blob, так что я просто, возможно, не написал это правильно, но он работает для меня.
На всякий случай, будьте осторожны с заменой: это синтаксис, чтобы сделать это глобальным, иначе он заменит только первый, который он встретит.

exportLogs : function(){
    var array = new Array();

    var str = $('#logs').html();
    array[0] = str.replace(/<br>/g, '\n\t');

    var blob = new Blob(array, {type: "text/plain;charset=utf-8"});
    saveAs(blob, "example.log");
}

Ответ 10

Я нашел два простых подхода, которые работают для меня. Сначала, используя уже нажатый элемент a и вводя данные загрузки. А во-вторых, генерируем элемент a с данными загрузки, выполняя a.click() и удаляя его снова. Но второй подход работает только при вызове действия щелчка пользователя. (Некоторые) Браузерный блок click() из других контекстов, например, при загрузке или срабатывании после таймаута (setTimeout).

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8">
    <script type="text/javascript">
      function linkDownload(a, filename, content) {
        contentType =  'data:application/octet-stream,';
        uriContent = contentType + encodeURIComponent(content);
        a.setAttribute('href', uriContent);
        a.setAttribute('download', filename);
      }
      function download(filename, content) {
        var a = document.createElement('a');
        linkDownload(a, filename, content);
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
      }
    </script>
   </head>
  <body>
    <a href="#" onclick="linkDownload(this, 'test.txt', 'Hello World!');">download</a>
    <button onclick="download('test.txt', 'Hello World!');">download</button>
  </body>
</html>

Ответ 11

Вот ссылка на метод URI данных, предложенный Мэтью, он работал на сафари, но не очень хорошо, потому что я не мог установить тип файла, он становится сохраненным как "неизвестный", а затем мне нужно вернуться туда позже и изменить это для просмотра файла...

http://www.nihilogic.dk/labs/canvas2image/

Ответ 12

Вы можете использовать localStorage. Это эквивалент файлов cookie Html5. Кажется, он работает на Chrome и Firefox, но на Firefox, мне нужно было загрузить его на сервер. То есть, тестирование непосредственно на моем домашнем компьютере не сработало.

Я работаю над примерами HTML5. Перейдите к http://faculty.purchase.edu/jeanine.meyer/html5/html5explain.html и прокрутите до лабиринта. Информация для восстановления лабиринта хранится с использованием localStorage.

Я пришел в эту статью, ища HTML5 JavaScript для загрузки и работы с XML файлами. Это то же самое, что и старый html и JavaScript????

Ответ 13

Как уже упоминалось, File API, а также FileWriter и FileSystem API-интерфейсы могут использоваться для хранения файлов на клиентской машине из контекста вкладки/окна браузера.

Однако есть несколько вещей, относящихся к последним двум API, о которых вы должны знать:

  • Реализации API-интерфейсов в настоящее время существуют только в браузерах на основе хрома (Chrome и Opera)
  • Оба API были сняты со стандартного трека W3C 24 апреля 2014 года и на данный момент являются собственностью
  • Удаление (теперь запатентованных) API-интерфейсов от реализации браузеров в будущем - это возможность
  • песочница (расположение на диске, за пределами которого файлы не могут произвести никакого эффекта) используется для хранения файлов, созданных с помощью API
  • Используется виртуальная файловая система (структура каталогов, которая необязательно существует на диске в той же форме, что и при доступе из браузера) представляет файлы, созданные с помощью API

Вот простые примеры того, как API используются, прямо или косвенно, в тандеме, чтобы сделать это:

BakedGoods *

bakedGoods.get({
        data: ["testFile"],
        storageTypes: ["fileSystem"],
        options: {fileSystem:{storageType: Window.PERSISTENT}},
        complete: function(resultDataObj, byStorageTypeErrorObj){}
});

Использование необработанных файлов, API FileWriter и FileSystem

function onQuotaRequestSuccess(grantedQuota)
{

    function saveFile(directoryEntry)
    {

        function createFileWriter(fileEntry)
        {

            function write(fileWriter)
            {
                var dataBlob = new Blob(["Hello world!"], {type: "text/plain"});
                fileWriter.write(dataBlob);              
            }

            fileEntry.createWriter(write);
        }

        directoryEntry.getFile(
            "testFile", 
            {create: true, exclusive: true},
            createFileWriter
        );
    }

    requestFileSystem(Window.PERSISTENT, grantedQuota, saveFile);
}

var desiredQuota = 1024 * 1024 * 1024;
var quotaManagementObj = navigator.webkitPersistentStorage;
quotaManagementObj.requestQuota(desiredQuota, onQuotaRequestSuccess);

Хотя API-интерфейсы FileSystem и FileWriter больше не соответствуют стандартным трекам, их использование может быть оправдано в некоторых случаях, на мой взгляд, потому что:

  • Возобновленный интерес у недействующих поставщиков браузеров может вернуть их обратно.
  • Проникновение на рынок реализующих (на основе хромовых) браузеров является высоким.
  • Google (основной вклад в Chromium) не дал и не закончил API API

Однако, если "некоторые случаи" охватывают ваши собственные, вы должны решить.

* BakedGoods поддерживается не кем иным, как этим парнем прямо здесь:)

Ответ 14

Вот учебник по экспорту файлов в формате ZIP:

Перед тем, как начать работу, есть библиотека для сохранения файлов, имя библиотеки - fileSaver.js. Здесь вы можете найти эту библиотеку. Давайте начнем. Теперь включите необходимые библиотеки:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.4/jszip.min.js"  type="text/javascript"></script>
<script type="text/javascript" src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.js" ></script>

Скопируйте этот код, и этот код загрузит zip файл с файлом hello.txt, имеющим контент Hello World. Если все работает нормально, это загрузит файл.

<script type="text/javascript">
    var zip = new JSZip();
    zip.file("Hello.txt", "Hello World\n");
    zip.generateAsync({type:"blob"})
    .then(function(content) {
        // see FileSaver.js
        saveAs(content, "file.zip");
    });
</script>

Это загрузит файл с именем file.zip. Вы можете прочитать здесь: http://www.wapgee.com/story/248/guide-to-create-zip-files-using-javascript-by-using-jszip-library

Ответ 15

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

Первым шагом для меня было создание двоичного двоичного объекта из данных, которые я сохранял. Существует множество примеров для выполнения этого для одного двоичного типа, в моем случае у меня есть двоичный формат с несколькими типами, который вы можете передать в виде массива для создания большого двоичного объекта.

saveAnimation: function() {

    var device = this.Device;
    var maxRow = ChromaAnimation.getMaxRow(device);
    var maxColumn = ChromaAnimation.getMaxColumn(device);
    var frames = this.Frames;
    var frameCount = frames.length;

    var writeArrays = [];


    var writeArray = new Uint32Array(1);
    var version = 1;
    writeArray[0] = version;
    writeArrays.push(writeArray.buffer);
    //console.log('version:', version);


    var writeArray = new Uint8Array(1);
    var deviceType = this.DeviceType;
    writeArray[0] = deviceType;
    writeArrays.push(writeArray.buffer);
    //console.log('deviceType:', deviceType);


    var writeArray = new Uint8Array(1);
    writeArray[0] = device;
    writeArrays.push(writeArray.buffer);
    //console.log('device:', device);


    var writeArray = new Uint32Array(1);
    writeArray[0] = frameCount;
    writeArrays.push(writeArray.buffer);
    //console.log('frameCount:', frameCount);

    for (var index = 0; index < frameCount; ++index) {

      var frame = frames[index];

      var writeArray = new Float32Array(1);
      var duration = frame.Duration;
      if (duration < 0.033) {
        duration = 0.033;
      }
      writeArray[0] = duration;
      writeArrays.push(writeArray.buffer);

      //console.log('Frame', index, 'duration', duration);

      var writeArray = new Uint32Array(maxRow * maxColumn);
      for (var i = 0; i < maxRow; ++i) {
        for (var j = 0; j < maxColumn; ++j) {
          var color = frame.Colors[i][j];
          writeArray[i * maxColumn + j] = color;
        }
      }
      writeArrays.push(writeArray.buffer);
    }

    var blob = new Blob(writeArrays, {type: 'application/octet-stream'});

    return blob;
}

Следующий шаг - заставить браузер предложить пользователю загрузить этот BLOB-объект с заранее заданным именем.

Все, что мне было нужно, это именованная ссылка, которую я добавил в HTML5, и которую я мог использовать повторно, чтобы переименовать исходное имя файла. Я держал это скрытым, так как ссылка не нуждается в отображении.

<a id="lnkDownload" style="display: none" download="client.chroma" href="" target="_blank"></a>

Последний шаг - предложить пользователю загрузить файл.

var data = animation.saveAnimation();
var uriContent = URL.createObjectURL(data);
var lnkDownload = document.getElementById('lnkDownload');
lnkDownload.download = 'theDefaultFileName.extension';
lnkDownload.href = uriContent;
lnkDownload.click();

Ответ 16

пытаться

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