Feature #7056 » zip-create-via-stream-v3.patch
app/controllers/attachments_controller.rb | ||
---|---|---|
137 | 137 |
end |
138 | 138 | |
139 | 139 |
def download_all |
140 |
Tempfile.create('attachments_zip-', Rails.root.join('tmp')) do |tempfile|
|
|
141 |
zip_file = Attachment.archive_attachments(tempfile, @attachments)
|
|
142 |
if zip_file
|
|
143 |
send_data(
|
|
144 |
File.read(zip_file.path),
|
|
145 |
:type => 'application/zip',
|
|
146 |
:filename => "#{@container.class.to_s.downcase}-#{@container.id}-attachments.zip")
|
|
147 |
else
|
|
148 |
render_404
|
|
149 |
end
|
|
140 |
zip_data = Attachment.archive_attachments(@attachments)
|
|
141 |
if zip_data
|
|
142 |
file_name = "#{@container.class.to_s.downcase}-#{@container.id}-attachments.zip"
|
|
143 |
send_data( |
|
144 |
zip_data,
|
|
145 |
:type => Redmine::MimeType.of(file_name),
|
|
146 |
:filename => file_name
|
|
147 |
)
|
|
148 |
else
|
|
149 |
render_404
|
|
150 | 150 |
end |
151 | 151 |
end |
152 | 152 |
app/models/attachment.rb | ||
---|---|---|
346 | 346 |
Attachment.where("created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age).destroy_all |
347 | 347 |
end |
348 | 348 | |
349 |
def self.archive_attachments(out_file, attachments)
|
|
349 |
def self.archive_attachments(attachments) |
|
350 | 350 |
attachments = attachments.select(&:readable?) |
351 | 351 |
return nil if attachments.blank? |
352 | 352 | |
353 | 353 |
Zip.unicode_names = true |
354 | 354 |
archived_file_names = [] |
355 |
Zip::File.open(out_file.path, Zip::File::CREATE) do |zip|
|
|
355 |
buffer = Zip::OutputStream.write_buffer do |zos|
|
|
356 | 356 |
attachments.each do |attachment| |
357 | 357 |
filename = attachment.filename |
358 | 358 |
# rename the file if a file with the same name already exists |
359 | 359 |
dup_count = 0 |
360 | 360 |
while archived_file_names.include?(filename) |
361 | 361 |
dup_count += 1 |
362 |
basename = File.basename(attachment.filename, '.*') |
|
363 | 362 |
extname = File.extname(attachment.filename) |
363 |
basename = File.basename(attachment.filename, extname) |
|
364 | 364 |
filename = "#{basename}(#{dup_count})#{extname}" |
365 | 365 |
end |
366 |
zip.add(filename, attachment.diskfile) |
|
366 |
zos.put_next_entry(filename) |
|
367 |
zos << IO.binread(attachment.diskfile) |
|
367 | 368 |
archived_file_names << filename |
368 | 369 |
end |
369 | 370 |
end |
370 |
out_file |
|
371 |
buffer.string |
|
372 |
ensure |
|
373 |
buffer&.close |
|
371 | 374 |
end |
372 | 375 | |
373 | 376 |
# Moves an existing attachment to its target directory |
test/unit/attachment_test.rb | ||
---|---|---|
280 | 280 | |
281 | 281 |
def test_archive_attachments |
282 | 282 |
attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1) |
283 |
Tempfile.create('attachments_zip', Rails.root.join('tmp')) do |tempfile| |
|
284 |
zip_file = Attachment.archive_attachments(tempfile, [attachment]) |
|
285 |
assert_instance_of File, zip_file |
|
283 |
zip_data = Attachment.archive_attachments([attachment]) |
|
284 |
file_names = [] |
|
285 |
Zip::InputStream.open(StringIO.new(zip_data)) do |io| |
|
286 |
while (entry = io.get_next_entry) |
|
287 |
file_names << entry.name |
|
288 |
end |
|
286 | 289 |
end |
290 |
assert_equal ['testfile.txt'], file_names |
|
287 | 291 |
end |
288 | 292 | |
289 | 293 |
def test_archive_attachments_without_attachments |
290 |
Tempfile.create('attachments_zip', Rails.root.join('tmp')) do |tempfile| |
|
291 |
zip_file = Attachment.archive_attachments(tempfile, []) |
|
292 |
assert_nil zip_file |
|
293 |
end |
|
294 |
zip_data = Attachment.archive_attachments([]) |
|
295 |
assert_nil zip_data |
|
294 | 296 |
end |
295 | 297 | |
296 | 298 |
def test_archive_attachments_should_rename_duplicate_file_names |
297 | 299 |
attachment1 = Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1) |
298 | 300 |
attachment2 = Attachment.create!(:file => uploaded_test_file("testfile.txt", ""), :author_id => 1) |
299 |
Tempfile.create('attachments_zip', Rails.root.join('tmp')) do |tempfile| |
|
300 |
zip_file = Attachment.archive_attachments(tempfile, [attachment1, attachment2]) |
|
301 |
Zip::File.open(zip_file.path) do |z| |
|
302 |
assert_equal ['testfile.txt', 'testfile(1).txt'], z.map(&:name) |
|
301 |
zip_data = Attachment.archive_attachments([attachment1, attachment2]) |
|
302 |
file_names = [] |
|
303 |
Zip::InputStream.open(StringIO.new(zip_data)) do |io| |
|
304 |
while (entry = io.get_next_entry) |
|
305 |
file_names << entry.name |
|
303 | 306 |
end |
304 | 307 |
end |
308 |
assert_equal ['testfile.txt', 'testfile(1).txt'], file_names |
|
305 | 309 |
end |
306 | 310 | |
307 | 311 |
def test_move_from_root_to_target_directory_should_move_root_files |