JavaScript: запись в поток загрузки

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


person Hephaestious    schedule 25.09.2016    source источник
comment
У вас есть алгоритм расшифровки данных?   -  person guest271314    schedule 25.09.2016
comment
Я планирую использовать AES.   -  person Hephaestious    schedule 25.09.2016
comment
Вы не можете писать в файлы на компьютере пользователя в браузере Javascript. Таким образом, вы не можете делать то, чего пытаетесь достичь, если вы не работаете, скажем, с Electron или чем-то подобным.   -  person Luke Joshua Park    schedule 25.09.2016
comment
@LukePark Вы не можете записывать файлы на компьютере пользователя в браузере Javascript. Технически запись в пользовательскую файловую систему возможна.   -  person guest271314    schedule 25.09.2016
comment
А как насчет метода, который описывает этот пост? Можно ли редактировать содержимое во время загрузки?   -  person Hephaestious    schedule 25.09.2016
comment
@ guest271314 Нет, это не так. Если вы не считаете, что кеш, локальное хранилище и т. д. записываются в файловую систему. Что это не так.   -  person Luke Joshua Park    schedule 25.09.2016
comment
comment
@Hephaestious Вы можете использовать ReadableStream, см. JS Promise — мгновенно получить некоторые данные из функции, которая возвращает Promise   -  person guest271314    schedule 25.09.2016
comment
Не знал, что такое существует. Хотя пока не уверен, что это решит проблему.   -  person Luke Joshua Park    schedule 25.09.2016
comment
Все еще не уверен, решит ли это проблему В чем проблема?   -  person guest271314    schedule 25.09.2016
comment
Похоже, это позволяет вам читать поток во время его загрузки, но я не вижу, как сохранить поток локально во время его обработки.   -  person Hephaestious    schedule 25.09.2016
comment
Что вы подразумеваете под изменить сохраняемые данные? Используйте выбранный метод расшифровки   -  person guest271314    schedule 25.09.2016
comment
Как бы вы затем сохранили эти данные локально по мере их обработки?   -  person Hephaestious    schedule 25.09.2016
comment
Вы можете хранить данные как Blob или ArrayBuffer, поскольку данные передаются в потоковом режиме после любых процессов расшифровки; затем используйте createObjectURL или data URI данных для загрузки пользователем после завершения потока   -  person guest271314    schedule 25.09.2016
comment
@guest271314 guest271314 - для этого потребуется завершить всю загрузку до того, как файл будет записан в локальную файловую систему - я думаю, что OP хочет передать входящие данные через какой-то компонент дешифрования и передать расшифрованные данные в файловую систему - так что the browser does not have to store hundreds of megabytes or several gigabytes in memory.   -  person Jaromanda X    schedule 25.09.2016
comment
@JaromandaX Правильно, мне нужно расшифровать и сохранить на лету.   -  person Hephaestious    schedule 25.09.2016
comment
Как можно сохранить файл на лету?   -  person guest271314    schedule 25.09.2016
comment
Записывая в поток загрузки или файл, последний из которых, как я знаю, JavaScript на самом деле не позволяет.   -  person Hephaestious    schedule 25.09.2016
comment
@Hephaestious — это надстройка или веб-расширение, которые являются для вас приемлемым вариантом (требуется, чтобы клиенты выбрали установку надстройки / расширения — что, конечно, означает отсутствие поддержки IE вообще)   -  person Jaromanda X    schedule 25.09.2016
comment
Должно быть возможно, используя nodejs. Здесь хотелось бы, чтобы весь поток был завершен и проверен, прежде чем предлагать файл для загрузки. Что, если 100 МБ файла в порядке, хотя последний байт поврежден?   -  person guest271314    schedule 25.09.2016
comment
У меня будет клиент C#, который может делать то же самое, но мне бы очень хотелось иметь веб-клиент, не имеющий внешних зависимостей.   -  person Hephaestious    schedule 25.09.2016
comment
@JaromandaX Почему это может быть проблемой? Злоумышленнику по-прежнему потребуется доступ к браузеру или системе пользователя для просмотра данных, не так ли?   -  person Hephaestious    schedule 25.09.2016
comment
@Hephaestious См. руководство по потоку . Обратите внимание, что обычно вы можете использовать browserify для использования nodejs в браузере.   -  person guest271314    schedule 25.09.2016
comment
@guest271314 guest271314 Я не вижу способа загрузить данные из этого потока в файловую систему клиента.   -  person Hephaestious    schedule 25.09.2016
comment
@JaromandaX Скорее всего, я буду использовать Стэнфордскую криптографическую библиотеку, поэтому я не думаю, что будет иметь значение, увидят ли люди, как данные шифруются или расшифровываются при использовании безопасного алгоритма, такого как AES. Пользователю по-прежнему нужно будет вводить пароль для расшифровки чего-либо.   -  person Hephaestious    schedule 25.09.2016
comment
@Hephaestious - нет проблем - я уберу шум в комментариях по этому поводу :p   -  person Jaromanda X    schedule 25.09.2016
comment
@Hephaestious Да, nodejs обычно не предназначен для использования в браузере. Пытался дать ссылку на иллюстрации использования .pipe(). Обработайте данные на сервере или даже Worker, затем предложите скачать. Опять же, не уверен, как файл может быть загружен в виде потока. Ближайшим просмотренным является файл .zip, который заполняется по мере загрузки. Каков размер файла, который будет загружен?   -  person guest271314    schedule 25.09.2016
comment
@guest271314 - What is the size of the file that will be downloaded? - это в вопросе   -  person Jaromanda X    schedule 25.09.2016
comment
@JaromandaX Хорошо. Понятно.   -  person guest271314    schedule 25.09.2016
comment
@Hephaestious Вы пробовали, не пытаясь загрузить загрузку? Браузер зависает?   -  person guest271314    schedule 25.09.2016
comment
Без потоковой загрузки это было бы очень просто: просто выполните ajax.get(url), расшифруйте данные и поместите их в тег привязки или одним из других способов загрузки указанной строки с помощью JavaScript. Я хочу расшифровать его на стороне клиента, как если бы я расшифровал его на стороне сервера, на самом деле нет никакого смысла использовать шифрование.   -  person Hephaestious    schedule 25.09.2016
comment
Не сосредотачиваясь здесь на части расшифровки, только на технической целесообразности потоковой загрузки. Вероятно, вы могли бы выполнить потоковую передачу в пользовательскую файловую систему, используя requestFileSystem, а затем использовать .toURL(), чтобы предложить загрузку. Хотя, по общему признанию, не пытались добавлять символы к набору data URI в элементе a href после того, как пользователь щелкнул якорь.   -  person guest271314    schedule 25.09.2016
comment
Не все браузеры поддерживают requestFileSystem.   -  person Hephaestious    schedule 25.09.2016
comment
Да это правда. Просто пытаюсь предложить возможные варианты для удовлетворения требований.   -  person guest271314    schedule 25.09.2016
comment
Во всех браузерах определены Blob и ArrayBuffer; хотя, опять же, не пробовал добавлять байты к Blob или ArrayBuffer, поскольку байты загружаются в локальный файл.   -  person guest271314    schedule 25.09.2016
comment
@Hephaestious На самом деле только что попытался добавить байты к элементу data URI после click в <a> с атрибутом download, и байты были добавлены к сохраненному файлу. Таким образом, это может быть возможно jsfiddle.net/6xazmmpp   -  person guest271314    schedule 25.09.2016
comment
Спасибо! Я собираюсь проверить это прямо сейчас.   -  person Hephaestious    schedule 25.09.2016
comment
@Hephaestious Пробовал с i < 100000, вкладка разбилась   -  person guest271314    schedule 25.09.2016
comment
Давайте продолжим обсуждение в чате.   -  person Hephaestious    schedule 25.09.2016


Ответы (2)


Это будет возможно только с комбинацией сервис-воркер + выборка + поток. В некоторых браузерах есть работник и выборка, но еще меньше поддерживают выборку с потоковой передачей (Blink)

new Response(new ReadableStream({...}))

Я создал библиотеку для сохранения потоковых файлов для связи с работником службы в другом месте для перехвата сетевого запроса: StreamSaver.js< /а>

Это немного отличается от потока узла, вот пример

function unencrypt(){
    // should return Uint8Array
    return new Uint8Array()
}

// We use fetch instead of xhr that has streaming support
fetch(url).then(res => {
    // create a writable stream + intercept a network response
    const fileStream = streamSaver.createWriteStream('filename.txt')
    const writer = fileStream.getWriter()

    // stream the response
    const reader = res.body.getReader()
    const pump = () => reader.read()
        .then(({ value, done }) => {
            let chunk = unencrypt(value)

            // Write one chunk, then get the next one
            writer.write(chunk) // returns a promise

            // While the write stream can handle the watermark,
            // read more data
            return writer.ready.then(pump)
        )

    // Start the reader
    pump().then(() =>
        console.log('Closed the stream, Done writing')
    )
})

Есть также два других способа получить потоковый ответ с помощью xhr, но они не являются стандартными и не имеют значения, если вы их используете (responseType = ms-stream || moz-chunked-arrayBuffer), потому что StreamSaver зависит от fetch + ReadableStream любыми способами и не может использоваться никаким другим образом

Позже вы сможете сделать что-то подобное, когда также будут реализованы потоки WritableStream + Transform.

fetch(url).then(res => {
    const fileStream = streamSaver.createWriteStream('filename.txt')

    res.body
        .pipeThrogh(unencrypt)
        .pipeTo(fileStream)
        .then(done)
})

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

window.onbeforeunload = function(e) {
  if( download_is_done() ) return

  var dialogText = 'Download is not finish, leaving the page will abort the download'
  e.returnValue = dialogText
  return dialogText
}
person Endless    schedule 25.09.2016
comment
Отсутствует реализация метода расшифровки? - person Naveen Ramawat; 17.02.2020
comment
да, потому что unencrypt может работать по-разному, взгляните на webcrypto, реализуйте свою собственную расшифровку - person Endless; 17.02.2020
comment
Привет, я использую angular и пробую то же решение, я не могу получить всплывающее окно загрузки, даже если я не видел никаких ошибок на консоли. - person Naveen Ramawat; 17.02.2020
comment
Я смог скачать в потоке, это спасение, большое спасибо - person Naveen Ramawat; 17.02.2020
comment
Использование StreamSaver JS на стороне клиента для потоковой передачи данных. Загружаемый файл зашифрован, а данные представлены в формате base64. Потоковые данные, возвращаемые из выборки, находятся в массиве Uint8Array. Есть ли способ изменить формат строки? Причина, по которой я спрашиваю, когда данные находятся в формате Unit8Array, мне трудно получить строку (данные в кодировке bas64) из Unit8Array, когда данные включают многобайтовые символы. - person Ranganatha; 18.02.2020
comment
не могли бы вы создать новую проблему в моем репо или опубликовать новый вопрос SO с примером и что пошло не так? - person Endless; 18.02.2020
comment
Спасибо за быстрый ответ Джимми Уортинг, У меня есть еще одна проблема. Моя система работает на самоподписанном сертификате. И я хочу обслуживать mitm.html со своего сервера, потому что при настройке клиента внешний Интернет может быть недоступен. Я пытался, но столкнулся с проблемой регистрации сервисного работника. Можно ли использовать mitm с локального сервера, который работает с использованием самоподписного сертификата? - person Naveen Ramawat; 19.02.2020

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

  1. window.open(Resourse_URL): загрузить ресурс в новом окне с Content_Disposition, установленным на "вложение";
  2. <a download href="path/to/resourse"></a>: использование атрибута "download" AnchorElement для загрузки потока на жесткий диск;

надеюсь, это поможет :)

person Jim    schedule 06.09.2019