如何在Rails 6中通过rubyzip实现含ActiveStorage文件的Zip打包下载功能
Rails 6 实现多层级目录Zip下载功能实操步骤
1. 安装依赖
首先在Gemfile中添加rubyzip依赖:
gem 'rubyzip', require: 'zip'
执行bundle install安装完成后重启服务。
2. 配置路由
在config/routes.rb中给listings添加成员路由:
resources :listings do member do get :download_zip end end
3. 编写控制器逻辑
在app/controllers/listings_controller.rb中添加download_zip方法,核心逻辑包含预加载关联数据避免N+1、流式生成Zip包直接返回给浏览器:
def download_zip # 预加载关联数据,避免N+1查询 @listing = Listing.includes(offers: :attachments).find(params[:id]) # 配置响应头,告知浏览器返回Zip下载文件 response.headers['Content-Type'] = 'application/zip' response.headers['Content-Disposition'] = "attachment; filename=\"#{@listing.name.parameterize}.zip\"" response.headers['Last-Modified'] = Time.now.httpdate.to_s # 流式生成Zip,不占用服务器本地磁盘存储 Zip::OutputStream.write_buffer do |zip| @listing.offers.each do |offer| # 生成Offer层级目录,parameterize处理特殊字符避免路径错误 offer_dir = "#{@listing.name.parameterize}/#{offer.id}_#{offer.title.parameterize}/" # 往Zip中写入目录 zip.put_next_entry(offer_dir) offer.attachments.each do |attachment| # 生成文件在Zip中的完整路径,加attachment.id避免同目录下重名文件覆盖 file_path = "#{offer_dir}#{attachment.id}_#{attachment.name.parameterize}" zip.put_next_entry(file_path) # 读取ActiveStorage文件内容写入Zip attachment.attachment_file.open do |file| zip.write file.read end end end end.tap do |zip_buffer| zip_buffer.rewind # 直接输出Zip流到浏览器 response.body = zip_buffer.read end end
4. 页面添加下载按钮
在app/views/listings/show.html.erb对应位置添加下载按钮,注意关闭Turbo/UJS的默认异步请求处理:
<%= link_to '下载Zip', download_zip_listing_path(@listing), class: 'btn btn-primary', data: { turbo: false, disable_with: '正在生成压缩包...' } %>
注意事项
- 若你的应用存在大文件、多附件的场景,建议将Zip生成逻辑放到Sidekiq等后台任务中执行,生成完成后给用户发下载通知,避免请求超时
- 需自行添加权限校验逻辑,确保只有有权限访问该Listing的用户才能触发下载操作
- 路径生成时统一使用
parameterize处理特殊字符,避免路径遍历漏洞和Zip打包异常
内容的提问来源于stack exchange,提问作者Seth
相关产品推荐
相关产品推荐

