Rails:以 zip 格式即时传输输出?
我需要在 zip 文件中提供数据库中的一些数据,动态流式传输,以便:
- 我不会将临时文件写入磁盘
- 我不会在 RAM 中编写整个文件
我知道我可以流式生成 zip使用 ZipOutputStream 作为 这里。我还知道我可以通过将 response_body
设置为 Proc
作为 此处。我认为我需要的是一种将这两件事结合在一起的方法。我可以让 Rails 提供来自 ZipOutputStream 的响应吗?我可以让 ZipOutputStream
为我提供增量数据块,并将其输入到我的 response_body
Proc
中吗?或者还有别的办法吗?
I need to serve some data from my database in a zip file, streaming it on the fly such that:
- I do not write a temporary file to disk
- I do not compose the whole file in RAM
I know that I can do streaming generation of zip files to the filesystemk using ZipOutputStream
as here. I also know that I can do streaming output from a rails controller by setting response_body
to a Proc
as here. What I need (I think) is a way of plugging those two things together. Can I make rails serve a response from a ZipOutputStream
? Can I get ZipOutputStream
give me incremental chunks of data that I can feed into my response_body
Proc
? Or is there another way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
短版本
https://github.com/fringd/zipline
长版本
所以 jo5h 的答案不起作用 不过,在 Rails 3.1.1 中,
我发现了一个 YouTube 视频对我有帮助。
http://www.youtube.com/watch?v=K0XvnspdPsc
问题的关键正在创建一个响应每个对象的对象...这就是我所做的:
编辑:
上面的解决方案实际上不起作用...问题是 rubyzip 需要在文件中跳转以重写条目的标头。特别是它需要在写入数据之前写入压缩大小。这在真正的流媒体情况下是不可能的......所以最终这个任务可能是不可能的。有可能一次缓冲整个文件,但这似乎不太值得。最终我只是写入了一个 tmp 文件...在heroku 上我可以写入 Rails.root/tmp 较少的即时反馈,虽然不理想,但有必要。
另一个编辑:
我最近有了另一个想法......如果我们不压缩文件,我们可以知道文件的压缩大小。计划是这样的:
按如下方式对 ZipStreamOutput 类进行子类化:
我还没有尝试实现这一点,但如果有任何成功,我会报告。
确定最后一次编辑:
在 zip 标准中: http://en.wikipedia.org/ wiki/Zip_(file_format)#File_headers
他们提到,您可以翻转一些内容,将大小、压缩大小和 crc 放在文件后面。所以我的新计划是对 zipoutput 流进行子类化,以便
写入大小和 CRC ,而且我需要获得所有 hack 以便在修复的 Rails 中流式输出...
无论如何,这一切都有效!
这是一颗宝石!
https://github.com/fringd/zipline
Short Version
https://github.com/fringd/zipline
Long Version
so jo5h's answer didn't work for me in rails 3.1.1
i found a youtube video that helped, though.
http://www.youtube.com/watch?v=K0XvnspdPsc
the crux of it is creating an object that responds to each... this is what i did:
EDIT:
the above solution didn't ACTUALLY work... the problem is that rubyzip needs to jump around the file to rewrite the headers for entries as it goes. particularly it needs to write the compressed size BEFORE it writes the data. this is just not possible in a truly streaming situation... so ultimately this task may be impossible. there is a chance that it might be possible to buffer a whole file at a time, but this seemed less worth it. ultimately i just wrote to a tmp file... on heroku i can write to Rails.root/tmp less instant feedback, and not ideal, but neccessary.
ANOTHER EDIT:
i got another idea recently... we COULD know the compressed size of the files if we do not compress them. the plan goes something like this:
subclass the ZipStreamOutput class as follows:
I haven't tried to implement this yet, but will report back if there's any success.
OK ONE LAST EDIT:
In the zip standard: http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
they mention that there's a bit you can flip to put the size, compressed size and crc AFTER a file. so my new plan was to subclass zipoutput stream so that it
furthermore i needed to get all the hacks in order to stream output in rails fixed up...
anyways it all worked!
here's a gem!
https://github.com/fringd/zipline
我有类似的问题。我不需要直接流式传输,但只有第一种情况不想写入临时文件。您可以轻松修改 ZipOutputStream 以接受 IO 对象而不仅仅是文件名。
从那里开始,只需在您的 Proc 中使用新的 Zip::IOOutputStream 即可。在你的控制器中,你可能会这样做:
I had a similar issue. I didn't need to stream directly, but only had your first case of not wanting to write a temp file. You can easily modify ZipOutputStream to accept an IO object instead of just a filename.
From there, it should just be a matter of using the new Zip::IOOutputStream in your Proc. In your controller, you'd probably do something like:
现在可以直接执行此操作:
It is now possible to do this directly:
这是您想要的链接:
http://info.michael-simons.eu/2008/01/21/using-rubyzip-to-create-zip-files-on-the-fly/
它构建并生成使用 ZipOutputStream 发送 zipfile,然后使用 send_file 将其直接从控制器发送出去。
This is the link you want:
http://info.michael-simons.eu/2008/01/21/using-rubyzip-to-create-zip-files-on-the-fly/
It builds and generates the zipfile using ZipOutputStream and then uses send_file to send it directly out from the controller.
对输出使用分块 HTTP 传输编码:HTTP 标头“Transfer-Encoding:分块”并根据分块编码规范重构输出,因此无需在传输开始时知道生成的 ZIP 文件大小。借助 Open3.popen3 和线程,可以轻松地用 Ruby 进行编码。
Use chunked HTTP transfer encoding for output: HTTP header "Transfer-Encoding: chunked" and restructure the output according to the chunked encoding specification, so no need to know the resulting ZIP file size at the begginning of the transfer. Can be easily coded in Ruby with the help of Open3.popen3 and threads.