使用carrierwave上传apk并存储apk和提取的图标

时间:2013-08-23 09:20:59

标签: ruby-on-rails apk carrierwave

我有一个具有多个版本的App模型 版本模型包含apkicon字段(等等)。

我目前有一个表单,允许用户使用carrierwave(class VersionUploader < CarrierWave::Uploader::Base)上传.apk文件。

上传apk文件后,我已使用ruby_apk gem提取图标(来自versions_uploader.rb内)。

对于图标,ruby_apk gem返回以下格式的哈希数组

{ "res/drawable-hdpi/ic_launcher.png" => "\x89PNG\x0D\x0A...", ... }

我想将图标保存到磁盘,并且可以像访问apk文件一样访问它(通过这个我的意思是可以调用像version.icon.identifier这样的东西)

我现在已经坚持了一段时间。任何有关如何完成或最佳方法的帮助将不胜感激。

提前致谢!

2 个答案:

答案 0 :(得分:1)

如果您使用两个不同的上传器,并从模型层设置图标数据,这将变得更加容易。模型代码可能如下所示:

mount_uploader :apk, ApkUploader
mount_uploader :icon, IconUploader
before_save :assign_icon

def assign_icon
  if apk_changed?
    icon_data = Android::Apk.new(apk.path).icons.values.first
    self.icon = StringIO.new(icon_data)
  end
end

请注意,before_save必须在mount_uploader :apk之后,因为mount_uploaderbefore_save事件上创建了its own callbacks,并且您希望在它们之后触发。< / p>

答案 1 :(得分:0)

我终于设法让这个工作了。 Taavo的回答让我朝着正确的方向前进,但它并没有像预期的那样发挥作用。

主要问题是StringIO.new(icon_data)一直给我no implicit conversion of nil into string错误。经过一番挖掘后,我发现我需要将original_filename作为属性添加到StringIO中,但它早已停止接受这一点而没有猴子修补类。我从Carrierwave wiki psge找到了解决这个问题的方法:How to: Upload from a string in Rails 3

现在让我将图标保存到文件系统,但神秘的是,没有使用引用填充Version.icon字段(Version.apk填充没有任何问题)。

最后,为了让它工作,我不得不摆脱before_Save :assign_icon回调并将我的代码移到ApkUploader文件中。

我的代码看起来像这样:

1)创建了一个继承自StringIO

的新初始化程序

配置/初始化/ stringiohax.rb

class AppSpecificStringIO < StringIO
  attr_accessor :filepath

  def initialize(*args)
    super(*args[1..-1])
    @filepath = args[0]
  end

  def original_filename
    File.basename(filepath)
  end
end

2)装载上传者

应用程序/模型/ version.rb

class Version < ActiveRecord::Base
  mount_uploader :apk, ApkUploader
  mount_uploader :icon, IconUploader

3)从APK中提取数据并保存图标属性

应用程序/模型/ version.rb

class ApkUploader < CarrierWave::Uploader::Base
  include Sprockets::Rails::Helper
  include CarrierWave::MimeTypes
  require 'ruby_apk'

  ..
  ...

  process :extract_apk_info

  def store_dir
    app = App.find(model.app_id)
    studio_name = Studio.find(app.studio_id).slug
    "uploads/#{studio_name}/#{app.id}/version_#{model.version_code}"
  end

  def extract_apk_info
    apk = Android::Apk.new(model.apk.path.to_s)
    manifest  = apk.manifest
    icons     = apk.icon

    icon_name = icons.keys.last
    icon_data = icons.values.last

    model.package_name = manifest.package_name
    model.version_code = manifest.version_code
    model.version_name = manifest.version_name
    model.min_sdk_ver  = manifest.min_sdk_ver
    model.icon         = AppSpecificStringIO.new(icon_name, icon_data)
  end