Fix #5363: Inconsistent order of files from zip uploads.
Upload files in natural order rather than archive order when uploading archive files. Before files were listed in the same order they appeared in the zip file. This could be in non-alphabetical order, or even with files from different directories interleaved between each other. Now files are uploaded in natural order, which is alphabetical order but with numbers sorted properly, so that `file-9.jpg` appears before `file-10.jpg`.
This commit is contained in:
32
app/logical/danbooru.rb
Normal file
32
app/logical/danbooru.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# The Danbooru module contains miscellaneous global helper functions.
|
||||
module Danbooru
|
||||
module EnumerableMethods
|
||||
extend self
|
||||
|
||||
# Sort a list of strings in natural order, e.g. with "file-2.txt" before "file-10.txt".
|
||||
#
|
||||
# @see https://en.wikipedia.org/wiki/Natural_sort_order
|
||||
# @see https://stackoverflow.com/a/15170063
|
||||
#
|
||||
# @param list [Enumerable<String>] The list to sort.
|
||||
# @return [Array] The sorted list.
|
||||
def natural_sort(list)
|
||||
natural_sort_by(list, &:to_s)
|
||||
end
|
||||
|
||||
# Sort a list of objects in natural order. The block should return a sort key, which is compared in natural order.
|
||||
#
|
||||
# @param list [Enumerable<Object>] The list to sort.
|
||||
# @return [Array] The sorted list.
|
||||
def natural_sort_by(list, &block)
|
||||
list.sort_by do |element|
|
||||
# "file-2022-10-01.txt" => ["file-", 2022, "-", 10, "-", 1, ".txt"]
|
||||
yield(element).to_s.split(/(\d+)/).map { |str| str.match?(/\A\d+\z/) ? str.to_i : str }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
extend EnumerableMethods
|
||||
end
|
||||
@@ -206,7 +206,7 @@ class Upload < ApplicationRecord
|
||||
tmpdir, filenames = file.extract!
|
||||
tmpdirs << tmpdir
|
||||
|
||||
filenames.map do |filename|
|
||||
Danbooru.natural_sort(filenames).map do |filename|
|
||||
name = "file://#{original_filename}/#{Pathname.new(filename).relative_path_from(tmpdir)}" # "file://foo.zip/foo/1.jpg"
|
||||
UploadMediaAsset.new(upload: self, file: filename, source_url: name)
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "danbooru"
|
||||
|
||||
module Danbooru
|
||||
module Extensions
|
||||
module String
|
||||
|
||||
BIN
test/files/archive/out-of-order.zip
Normal file
BIN
test/files/archive/out-of-order.zip
Normal file
Binary file not shown.
@@ -445,6 +445,19 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal(5, upload.upload_media_assets.size)
|
||||
assert_equal("file://ugoira.zip/000000.jpg", upload.upload_media_assets[0].source_url)
|
||||
end
|
||||
|
||||
should "upload the files in filename order" do
|
||||
upload = assert_successful_upload("test/files/archive/out-of-order.zip", user: @user)
|
||||
|
||||
assert_equal(6, upload.media_asset_count)
|
||||
assert_equal(6, upload.upload_media_assets.size)
|
||||
assert_equal("file://out-of-order.zip/9/9.gif", upload.upload_media_assets[0].source_url)
|
||||
assert_equal("file://out-of-order.zip/9/10.gif", upload.upload_media_assets[1].source_url)
|
||||
assert_equal("file://out-of-order.zip/9/11.gif", upload.upload_media_assets[2].source_url)
|
||||
assert_equal("file://out-of-order.zip/10/9.gif", upload.upload_media_assets[3].source_url)
|
||||
assert_equal("file://out-of-order.zip/10/10.gif", upload.upload_media_assets[4].source_url)
|
||||
assert_equal("file://out-of-order.zip/10/11.gif", upload.upload_media_assets[5].source_url)
|
||||
end
|
||||
end
|
||||
|
||||
context "uploading a .rar file from your computer" do
|
||||
|
||||
Reference in New Issue
Block a user