zip_tricks не будет получать дальнейших обновлений и поддержки и больше не будет поддерживаться. История zip_tricks продолжается в zip_kit, который будет получать регулярные обновления и поддерживает все функции zip_tricks (и даже больше). Благодарим вас за участие в сообществе zip_tricks!
Позволяет осуществлять потоковое воспроизведение ZIP-файлов без перемотки из Ruby.
Первоначально написанный как духовный преемник зиплайна, а теперь с гордостью использующий его под капотом.
Позволяет записывать ZIP-архив в файл, сокет, строку или массив без необходимости перематывать его в любой момент. Используется для создания очень больших ZIP-архивов для немедленной отправки клиентам или для записи больших ZIP-архивов без увеличения памяти.
zip_tricks в настоящее время удовлетворяет все наши потребности в архивировании (миллионы ZIP-файлов, создаваемых в день), поэтому мы вполне уверены, что он широко совместим с большим количеством приложений для разархивирования конечных пользователей.
Поддержка синтаксиса Ruby 2.1+ (аргументы ключевых слов со значениями по умолчанию) и рабочая библиотека zlib (все это также доступно для jRuby). jRuby может столкнуться с проблемами при использовании методов чтения из-за того, что размер аргумента IO#seek
ограничен 32 битами.
Самый простой — включить в контроллер модуль ZipTricks::RailsStreaming
.
class ZipsController < ActionController :: Base
include ZipTricks :: RailsStreaming
def download
zip_tricks_stream do | zip |
zip . write_deflated_file ( 'report1.csv' ) do | sink |
CSV ( sink ) do | csv_write |
csv_write << Person . column_names
Person . all . find_each do | person |
csv_write << person . attributes . values
end
end
end
zip . write_deflated_file ( 'report2.csv' ) do | sink |
...
end
end
end
end
Если вам нужны дополнительные удобства, вы также можете использовать zipline, который будет автоматически обрабатывать и передавать вложения (Carrierwave, Shrine, ActiveStorage) и удаленные объекты через HTTP.
Основной вариант использования — сжатие на лету. Некоторые данные будут буферизованы дефлятором Zlib, но раздувание памяти будет очень ограниченным. Данные будут записываться в пункт назначения через довольно регулярные промежутки времени. Сжатие Deflate лучше всего подходит для таких вещей, как текстовые файлы.
out = my_tempfile # can also be a socket
ZipTricks :: Streamer . open ( out ) do | zip |
zip . write_stored_file ( 'mov.mp4.txt' ) do | sink |
File . open ( 'mov.mp4' , 'rb' ) { | source | IO . copy_stream ( source , sink ) }
end
zip . write_deflated_file ( 'long-novel.txt' ) do | sink |
File . open ( 'novel.txt' , 'rb' ) { | source | IO . copy_stream ( source , sink ) }
end
end
К сожалению, при таком подходе невозможно вычислить размер выводимого ZIP-файла, поскольку вы не знаете, насколько большими будут сегменты сжатых данных.
Чтобы «извлечь» данные из ZipTricks, вы можете создать объект OutputEnumerator
, который будет выдавать двоичные фрагменты по частям, а также применять некоторую буферизацию. Поскольку этот OutputEnumerator
отвечает на #each
и выдает строки, его также можно (и нужно!) использовать в качестве тела ответа Rack. Верните его на свой веб-сервер, и ваш ZIP будет транслироваться. Блок, который вы передаете OutputEnumerator
начнет выполняться только после того, как ваше тело ответа начнет повторяться - при фактической отправке ответа клиенту (если вы не используете буферизующий веб-сервер Rack, такой как Webrick).
body = ZipTricks :: Streamer . output_enum do | zip |
zip . write_stored_file ( 'mov.mp4' ) do | sink | # Those MPEG4 files do not compress that well
File . open ( 'mov.mp4' , 'rb' ) { | source | IO . copy_stream ( source , sink ) }
end
zip . write_deflated_file ( 'long-novel.txt' ) do | sink |
File . open ( 'novel.txt' , 'rb' ) { | source | IO . copy_stream ( source , sink ) }
end
end
[ 200 , { } , body ]
Используйте SizeEstimator
, чтобы вычислить правильный размер результирующего архива.
# Precompute the Content-Length ahead of time
bytesize = ZipTricks :: SizeEstimator . estimate do | z |
z . add_stored_entry ( filename : 'myfile1.bin' , size : 9090821 )
z . add_stored_entry ( filename : 'myfile2.bin' , size : 458678 )
end
# Prepare the response body. The block will only be called when the response starts to be written.
zip_body = ZipTricks :: RackBody . new do | zip |
zip . add_stored_entry ( filename : "myfile1.bin" , size : 9090821 , crc32 : 12485 )
zip << read_file ( 'myfile1.bin' )
zip . add_stored_entry ( filename : "myfile2.bin" , size : 458678 , crc32 : 89568 )
zip << read_file ( 'myfile2.bin' )
end
[ 200 , { 'Content-Length' => bytesize . to_s } , zip_body ]
Вам не обязательно «скармливать» все содержимое файлов, которые вы положили в архив, через объект Streamer. Если местом назначения записи для вашего варианта использования является Socket
(скажем, вы пишете с использованием Rack hijack) и вам заранее известны метаданные файла (CRC32 несжатого файла и размеры), вы можете писать непосредственно в этот сокет, используя некоторую ускоренную технику записи и использовать Streamer только для записи метаданных ZIP.
# io has to be an object that supports #<<
ZipTricks :: Streamer . open ( io ) do | zip |
# raw_file is written "as is" (STORED mode).
# Write the local file header first..
zip . add_stored_entry ( filename : "first-file.bin" , size : raw_file . size , crc32 : raw_file_crc32 )
# Adjust the ZIP offsets within the Streamer
zip . simulate_write ( my_temp_file . size )
# ...and then send the actual file contents bypassing the Streamer interface
io . sendfile ( my_temp_file )
end
Проверьте каталог examples/
в корне проекта. Это даст вам хорошее представление о различных вариантах использования, которые поддерживает библиотека.
BlockCRC32
вычисляет контрольную сумму CRC32 ввода-вывода в потоковом режиме. Это немного удобнее для этой цели, чем использование необработанных функций библиотеки Zlib.
crc = ZipTricks :: StreamCRC32 . new
crc << next_chunk_of_data
...
crc . to_i # Returns the actual CRC32 value computed so far
...
# Append a known CRC32 value that has been computed previosuly
crc . append ( precomputed_crc32 , size_of_the_blob_computed_from )
Вы также можете вычислить CRC32 для всего объекта ввода-вывода, если он отвечает на #eof?
:
crc = ZipTricks :: StreamCRC32 . from_io ( file ) # Returns an Integer
Библиотека содержит модуль чтения, поиграйтесь с ним и посмотрите, что возможно. Это не полноценная программа для чтения ZIP-файлов, но она была разработана для определенной цели (высокопараллельная распаковка удаленно хранящихся ZIP-файлов) и поэтому выполняет свою функцию довольно хорошо. Пожалуйста, остерегайтесь последствий для безопасности использования программ чтения ZIP, которые не прошли формальную проверку (наша не была проверена).
main
, чтобы убедиться, что эта функция еще не реализована или ошибка еще не исправлена.Авторские права (c) 2020 WeTransfer.
zip_tricks
распространяется на условиях лицензии Гиппократа. Дополнительную информацию см. в файле LICENSE.txt. Если эта лицензия неприемлема для вашего варианта использования, мы по-прежнему поддерживаем дерево версий 4.x, которое остается под лицензией MIT, для получения дополнительной информации см. https://rubygems.org/gems/zip_tricks/versions. Обратите внимание, что мы переносим в это дерево только некоторые оптимизации производительности и важные исправления ошибок, но не новые функции.