Как распаковать заархивированную папку с помощью rubyzip

Я знаю, как получить содержимое обычного zip-файла с помощью rubyzip. Но у меня возникли проблемы с распаковкой содержимого заархивированной папки, и я надеюсь, что кто-нибудь из вас, ребята, может мне помочь.

это код, который я использую для распаковки:

Zip::ZipFile::open(@file_location) do |zip|
 zip.each do |entry|
  next if entry.name =~ /__MACOSX/ or entry.name =~ /\.DS_Store/ or !entry.file?
  logger.debug "#{entry.name}"
  @data = File.new("#{Rails.root.to_s}/tmp/#{entry.name}")
 end
end

entry.name дает мне имя файла внутри zip-файла. Это отлично работает с обычным zip-файлом. Но когда zip-файл создается из папки, то название записей примерно такое: test-folder/test.pdf. Когда я затем пытаюсь создать файл, он говорит мне, что файл не может быть найден. Вероятно, это связано с тем, что он находится внутри папки «test», которая находится внутри zip.

Если я проверю, что запись является папкой, папка не может быть найдена. Поэтому я подумал, что решение состоит в том, чтобы прочитать запись как поток, а затем сохранить ее как файл. Получить входной поток легко, но как сохранить его в виде файла? Это то, что я получил до сих пор.

Zip::ZipFile::open(@file_location) do |zip|
 zip.each do |entry|
  next if entry.name =~ /__MACOSX/ or entry.name =~ /\.DS_Store/ or !entry.file?
  logger.debug "#{entry.name}"
  @data = entry.get_input_stream.read
  # How do i create a file from a stream?
 end
end

В основном мой вопрос: как я могу создать файл из потока? Или есть более простой подход к этому, чем мой?

===EDIT=== Я использую скрепку для хранения файлов.


person grub4r    schedule 16.05.2011    source источник


Ответы (3)


Я обнаружил, что более простой подход, основанный на jhwist, работает нормально:

Zip::File.open(@file_location) do |zipfile|
  zipfile.each do |entry|
    # The 'next if...' code can go here, though I didn't use it
    unless File.exist?(entry.name)
      FileUtils::mkdir_p(File.dirname(entry.name))
      zipfile.extract(entry, entry.name) 
    end
  end
end

Условие, очевидно, является необязательным, но без него код выдаст ошибку, если попытается перезаписать существующий файл.

person Arepo    schedule 15.04.2015
comment
Этот подход не сохраняет права доступа к файлам. Как я могу сохранить разрешения после извлечения? - person itsh; 02.09.2015

Я думаю, что ваша проблема не в том, нужно ли вам писать файл из потока или нет. По сути, если вы вызовете File.new, он создаст новый поток ввода-вывода (File является подклассом IO). Поэтому все, что вы хотите сделать с потоком из zip-файла, также должно работать с обычным файлом.

Когда ты говоришь

Когда я пытаюсь создать файл, он говорит мне, что файл не может быть найден

Я думаю, что происходит то, что родительский каталог для файла, который вы хотите создать, не существует (в вашем случае test-folder). Что вы хотите сделать, это что-то вроде этого (не проверено):

Zip::ZipFile::open(@file_location) do |zip|
 zip.each do |entry|
   next if entry.name =~ /__MACOSX/ or entry.name =~ /\.DS_Store/ or !entry.file?
   logger.debug "#{entry.name}"
   FileUtils::mkdir_p(File.dirname(entry.name)) # might want to check if it already exists    
   @data = File.new("#{Rails.root.to_s}/tmp/#{entry.name}")
 end
end
person jhwist    schedule 16.05.2011
comment
Я попробовал ваш код, но он создает только родительскую папку записи в корне моего проекта. Я все еще не могу получить доступ к записи сам по себе. На самом деле это Errno::ENOENT - я подумал, что должен упомянуть об этом. спасибо - person grub4r; 17.05.2011

Я решил это, используя поток и создав StringIO. Вот код

Zip::ZipFile::open(@file_location) do |zip|
 zip.each do |entry|
  next if entry.name =~ /__MACOSX/ or entry.name =~ /\.DS_Store/ or !entry.file?

  begin
   # the normal unzip-code
  rescue Errno::ENOENT
   # when the entry can not be found
   @data = entry.get_input_stream.read
   @file = StringIO.new(@data)
   @file.class.class_eval { attr_accessor :original_filename, :content_type }
   @file.original_filename = entry.name
   @file.content_type = MIME::Types.type_for(entry.name)

   # save it / whatever
  end
 end
end
person grub4r    schedule 17.05.2011
comment
Эта первая строка должна быть: Zip::ZipFile.open(@file_location) do |zip| - person lightyrs; 13.08.2013