Файл Ruby ZIP Temp имеет тип содержимого PDF

Я использую рельсы 6.1 и скрепку 5.0.

Я создал работника для создания ZIP-файла на основе других файлов, хранящихся в S3, а затем загрузил этот ZIP-файл обратно в S3.

Итак, в основном я загружаю файлы с S3, добавляю их в этот временный ZIP-архив, а затем сохраняю ZIP-архив в S3.

class Download < ApplicationRecord
  # ...
  has_attached_file :file,
                    path: "#{ENV['DEVELOPER']}/Institucion-:institution_id/Usuario-:user_id/:id/:filename",
                    content_type: { content_type: ["application/zip"] }

  # ...
end 

class GenerateZipWorker < ApplicationWorker
  def perform(download_id, options = {})
    @download = Download.find(download_id)
    # ...
    @download.file = generate_full_zip
    # Here the ZIP Temp file gets a PDF content-type
    # @download.file.content_type
    # => "application/pdf"
    @download.file_file_name = "#{@postulation_template.name[0..100].parameterize}.zip"
    @download.save

  end

  private
  def generate_full_zip
    temp_zip_file = Tempfile.new("#{@postulation_template.name[0..100].parameterize}.zip")

    Zip::File.open(temp_zip_file.path, Zip::File::CREATE) do |zip|
      @postulations.each do |postulation|
        generate_postulation_file(zip, postulation)
      end
    end

    temp_zip_file.rewind
    temp_zip_file
  end

  def generate_postulation_file(zip, postulation)
    root_path = postulation.file_name
    zip.get_output_stream(File.join(root_path, postulation.postulation_pdf_file_name)) { |f| f.write postulation.postulation_pdf.s3_object.get.body.string }
  end
end

Я попытался вручную установить тип контента перед сохранением файла.

@download.file_content_type = 'application/zip'

Но когда я загружаю файл с панели управления AWS S3, он загружает его в формате PDF, добавляя .pdf в конце имени файла (foo.zip.pdf). Если я удалю расширение PDF, сохранив ZIP-файл, загруженный файл будет работать нормально. Если я изменю тип контента на панели управления AWS S3 с application/pdf на application/zip, я смогу загрузить без проблем.

1️Как загрузить файл с типом содержимого application/zip?

2️⃣Это временный файл портит content-type?

3️⃣Есть ли лучший способ создать временный ZIP-файл?

Здесь вам вообще не нужен временный файл. Вместо этого вы можете просто использовать Zip::OutputStream.

max 04.06.2024 15:46

Я реорганизовал его с помощью Zip::OutputStream, но все еще получаю тип контента в формате PDF.

Pedro 04.06.2024 17:20

Если я правильно понимаю, вы архивируете несколько файлов .pdf. Если это так, то вы имеете дело с неправильным определением типа MIME. О подобных ошибках сообщалось и раньше, например: github.com/mimemagicrb/mimemagic/issues/49 (файл .pdf внутри файла .zip вызывал неправильное определение типа файла). Я предполагаю, что это ошибка в стороннем коде.

mate 04.06.2024 18:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
3
106
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Судя по всему, Paperclip неправильно определяет MIME-тип файла.

Вы правильно попытались вручную установить тип контента перед его сохранением, но у вас это не сработало, поскольку Paperclip фактически считывает тип контента из другого объекта.

Если вам интересно, вы можете проверить исходные файлы Paperclip. В экземпляре Attachment создана переменная экземпляра @file, которая, в свою очередь, имеет переменную экземпляра @content_type, и именно здесь Paperclip считывает тип контента перед загрузкой его в S3.

Итак, что вам нужно исправить, так это сделать следующее:

@download.file_content_type = 'application/zip'
@download.file.instance_variable_get(:"@file").instance_variable_set(:"@content_type", "application/zip")
@download.save

Вы можете проверить это, прежде чем обновлять переменную экземпляра, как я указал, этот код должен вернуть application/pdf (даже если вы обновили @download.file_content_type):

@download.file.instance_variable_get(:"@file").content_type

После того, как вы установите переменную, как я написал, она вернется application/zip, как и ожидалось.

> Papertrail фактически считывает тип контента из другого объекта. Вы имеете в виду скрепку, верно?

Pedro 10.06.2024 13:54

Кажется, что способ сделать это скрепкой: @download.file.instance_write(:content_type, 'application/zip')

Pedro 10.06.2024 17:23

Да, Papertrail — опечатка (обновлено), мы тоже используем его в нашем проекте :)

GProst 10.06.2024 19:33

попробуйте установить validates_attachment_content_type для :file

class Download < ApplicationRecord
  has_attached_file :file
  validates_attachment_content_type :file, content_type: /\A.*\z/
end

P.S. рабочий прототип ниже:

######### CONFIGURATION START #########
require 'active_record'
require 'paperclip'
require 'paperclip/railtie'
require 'paperclip/schema'
require 'zip'

ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")

ActiveSupport.on_load :active_record do
  Paperclip::Railtie.insert
end

class FakeRails
  def initialize(env, root)
    @env = env
    @root = root
  end

  attr_accessor :env, :root

  def const_defined?(const)
    false
  end
end

Rails = FakeRails.new('test', Pathname.new('./').join('tmp'))
######### CONFIGURATION END #########

######## APP START #############
class Download < ActiveRecord::Base
  connection.create_table table_name, force: true do |t|
    t.string :name
  end
  connection.add_attachment(:downloads, :postulation)
  connection.add_attachment(:downloads, :file)

  has_attached_file :postulation
  validates_attachment_content_type :postulation, content_type: /\Aimage/.*\z/

  has_attached_file :file
  validates_attachment_content_type :file, content_type: /\A.*\z/
end

##### SEED DATA START ###
@download = Download.create! name: 'bob'
@download.postulation = File.open('./test.jpg')
@download.save!
##### SEED DATA END ###

##### CONTROLLER START ####
temp_zip_file = Tempfile.new('test.zip')
Zip::File.open(temp_zip_file.path, Zip::File::CREATE) do |zip|
  Download.all.each do |postulation|
    zip.get_output_stream(File.join('./', 'somename.pdf')) { |f| f.write('Some Content') }
  end
end
temp_zip_file.rewind
@download.file = temp_zip_file
@download.file_file_name = 'myzip.zip'
@download.save!

puts @download.file_file_name # => myzip.zip
puts @download.file_content_type # => application/zip
##### CONTROLLER END ####

######## APP END #############

P.S. вы можете запустить прототип с помощью

  1. сохраните код в: test.rb файле
  2. создайте общедоступный каталог с помощью: mkdir public tmp
  3. скопируйте какой-нибудь jpg-файл cp ~/Downloads/test.jpg .
  4. запустите его с помощью: ruby test.rb

Другие вопросы по теме

Похожие вопросы

Как использовать Rails и Hotwire Turbo Streaming для потоковой передачи длинной страницы?
В MyEngine::FrontendController#index отсутствует шаблон для форматов запросов: text/html отображает макет, отличный от контроллера
Docker: ответ об ошибке от демона: не удалось создать задачу для контейнера: не удалось создать задачу прокладки: среда выполнения OCI создала неудачные рельсы
Измените ответ турбопотока на клиенте перед вызовом renderStreamMessage
Как реализовать обратный вызов «around_perform» после включения Sidekiq (Sidekiq::Job) внутри ApplicationJob
Ошибка развертывания Rails 7 на Render.com во время assets:precompile - ActionView::Template::Error (Ассет __ отсутствует в конвейере ресурсов.)
Исключение Rails: такой таблицы не существует
Как пропустить before_action для всех спецификаций рельсов
Могу ли я получить вложение ActiveStorage через отношение в одном простом запросе?
Image_tag не возвращает изображение, используя URL-адрес в сервисе