Возможно ли передать поток октетов, создаваемый в javascript?

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

Это возможно, используя загрузку октетного потока, помещая ее как href, как указано в этом ответе:

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

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

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

Можно ли передавать текст по мере его создания, используя ТОЛЬКО ЛОГИКУ КЛИЕНТА?

Например:

var inputString = "A";
var outStr = "";
for(var i = 0; i < 10000000 ; i++)
 {
   /* concatenate inputString to output on the go */
 }

Ответы

Ответ 1

Да и нет. Нет, потому что нет способа писать в файлы с помощью только javascript на стороне клиента. Вроде. Вы можете попросить пользователя загрузить и сохранить файл, но, как вы упомянули, код должен сгенерировать весь файл до того, как это произойдет. Примечание: "Потоком" я предполагаю, что вы имеете в виду поток для файла (постоянно пишите в файл) и "ТОЛЬКО КЛИЕНТСКАЯ БОКОВАЯ ЛОГИКА". Я предполагаю, что вы имеете в виду в браузере.

Похоже, Mozilla работает над тем, чтобы клиентский код взаимодействовал с файлами. Вот да. Вид. У них есть собственная файловая система api, которая позволяет вам взаимодействовать с файловой системой локальных машин (писать). В частности, есть функция, которая позволяет записывать входной поток в файл. Однако есть несколько звездочек:

1) выглядит так, что вся система устарела; они рекомендуют разработчикам использовать OS.file для файлов ввода/вывода

2) Вы должны использовать XPConnect, систему, которая позволяет вам обращаться к Mozilla XPCOM (библиотеке компонентов) в javascript. Если вы хотите сделать это в браузере, похоже, что только расширения Firefox имеют соответствующие разрешения для взаимодействия с этими компонентами(). Если вы не хотите делать это в браузере, вы, очевидно, можете просто использовать node.

Разумеется, при внедрении неизбежно появятся дополнительные сложности. Но это выглядит как самый верный путь вперед, видя, как OS.File предоставляет вам доступ к таким функциям, как OS.File.writeAtomic() и базовый записать в файл

Говоря это, это не так уж и много, но, надеюсь, это дает вам прочную отправную точку. Как упоминал @dandavis, браузеры (т.е. Логика на стороне клиента) предназначены для того, чтобы не допускать такого рода вещи. Это был бы невероятно большой недостаток надзора/безопасности, если бы веб-сайт мог взаимодействовать с любой локальной файловой системой пользователя.

Дополнительные ресурсы:
Википедия на XPConnect
Руководство по работе с XPCOM в javascript - возможно, не так полезно

Ответ 2

Существует способ сделать это, но он зависит от API только для файловой системы Chrome. Мы создадим и напишем во временный файл в изолированной файловой системе и скопируем его в обычную файловую систему, как только мы закончим. Таким образом, вам не нужно хранить весь файл в памяти. Асинхронная версия API Chrome в настоящее время не рассматривается для стандартизации W3C, но синхронная версия (которая использует веб-работников). Если поддержка браузера вызывает беспокойство, то этот ответ не для вас.

API работает следующим образом: Во-первых, мы получаем функцию requestFileSystem() из браузера. В настоящее время он имеет префикс "webkit":

window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;

Затем мы запрашиваем временную файловую систему (таким образом, нам не нужно запрашивать разрешение пользователя):

var fileSystem; //This will store the fileSystem for later access
var fileSize = 1024*1024 //Our maximum file system size.
function errorHandler(e) {
  console.log('Error: ' + e.name);
}
window.requestFileSystem(window.TEMPORARY, fileSize, function (fs) { fileSystem = fs; }, errorHandler);

Теперь, когда у нас есть доступ к файловой системе, пришло время создать файл:

var fileOptions = {
    create: true, //If the file is not found, create it
    exclusive: false //Don't throw an error if the file doesn't exist
};

Здесь мы вызываем функцию getFile(), которая может создать файл, если он не существует. Внутри обратного вызова мы можем создать новый fileWriter для записи в файл. Затем fileWriter перемещается в конец файла, и мы создаем новый блок текста для добавления к нему.

fileSystem.root.getFile(fileName, fileOptions, function(fileEntry) {
    fileEntry.createWriter(function(fileWriter) {
      fileWriter.seek(fileWriter.length); 
      var blob = new Blob([STRING_TO_WRITE], {type: 'text/plain'});
      fileWriter.write(blob);
    }, errorHandler);
});

Обратите внимание, что этот API не сохраняется в нормальной файловой системе пользователя. Вместо этого он сохраняет специальную папку с песочницей. Если вы хотите сохранить его в пользовательской файловой системе, вы можете создать ссылку filesystem:. Когда пользователь нажмет на него, он предложит им сохранить его. После их сохранения вы можете удалить временный файл.

Эта функция генерирует ссылку filesystem с помощью функции fileEntry toURL():

var save = function () {
  var download = document.querySelector("a[download]");
  if (!fileSystem) { return; }
   fileSystem.root.getFile(fileName, {create: false, exclusive: true}, function(fileEntry) {
    download.href = fileEntry.toURL();
  }, errorHandler);
}

Использование ссылки с атрибутом загрузки приведет к загрузке файла.

<a download></a>

Вот плункер, который демонстрирует это: http://plnkr.co/edit/q6ihXWEXSOtutbEy1b5G?p=preview

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

Для получения дополнительной информации см. статью HTML5rocks или этот, если вы хотите использовать новый, синхронный API веб-рабочих.

Ответ 3

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

Но есть новый способ, который можно использовать сегодня (за флагом), но будет включен в следующей версии chrome (52)

И вот где я сделаю ответ @KeenanLidral-Porter неправильным. И @quantumwannabe ответьте на ненужный шаг
Поскольку теперь есть способ напрямую написать поток в файловую систему: StreamSaver.js

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

const writeStream = streamSaver.createWriteStream('filename.txt')
const encoder = new TextEncoder
let data = 'a'.repeat(1024) // Writing some stuff triggers the save dialog to show
let uint8array = encoder.encode(data + "\n\n")

writeStream.write(uint8array) // Write some data when you got some
writeStream.close() // End the saving