Возможно ли передать поток октетов, создаваемый в 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