Rails Gem开发(一)——Carrierwave 实现文件上传

按照项目需求,需要实现从本地上传文件到rails服务器的功能。我们选择使用carrierwave来实现这一功能。

github:https://github.com/carrierwaveuploader/carrierwave

开发前需要明确的问题

在开发时,我们不能盲目的去实现功能。为此,在实践之前,我们需要提出一些问题来引导我们解决问题:

  • 整个文件上传的流程是怎样的?
  • carrierwave 扮演了流程中的什么角色?
  • carrierwave 提供了哪些上传文件的接口?
  • 我们需要实现那些功能来完成整个系统?

提出这些问题,是为了让自己能够更深刻地理解整个系统的工作,在整体上把握,而不仅仅是把功能实现。

问题解决

rails文件上传流程

我们需要结合rails 服务器的MVC框架来分析文件上传的整个流程。

我们需要将本地的文件上传到服务器,那么首先我们需要一个界面(view层),这个界面上提供读取本地文件和上传文件的功能。浏览器通过发送http请求来访问服务器,我们需要上传的文件信息也应该存在请求中。

http请求通过路由(routing)选择特定的控制器(controller),执行特定的动作(action)来获取http请求中的文件信息,获取到信息后我们即可执行将文件存储到特定位置的操作了,但MVC框架告诉我们不应该这么做,数据逻辑和业务逻辑都应该放在model中执行。

我们还需要一个model来进行业务逻辑的实现,获得的信息我们既可以存到数据库中,也可以存到服务器的任意位置。这里,我们需要将文件存储到服务器的特定位置,而不需要存储到数据库中。因此,我们的model实际是不需要对应table(表)的,只需要在model层实现存储文件于特定位置的功能即可。

carrierwave 在流程中的实现

理解了rails服务器中文件上传的流程,我们再来看看carrierwave能为我们实现什么功能。我们来看github中的描述:

Getting Started

Start off by generating an uploader:

rails generate uploader Avatar

this should give you a file in:

app/uploaders/avatar_uploader.rb

从中我们可以看到,carrierwave为我们实现的是model层的功能,也就是实现的是将文件存储到rails服务器中的功能。

carrierwave 提供的部分重要接口

  • 文件存储接口:
uploader = AvatarUploader.new
uploader.store!(my_file)
  • 文件存储位置接口:
class Avatar < CarrierWave::Uploader::Base
    def store_dir
        'public/my/upload/directory'
    end
end
  • 临时文件存储位置接口:
class Avatar < CarrierWave::Uploader::Base
    def cache_dir
        '/tmp/projectname-cache'
    end
end
  • 文件格式白名单接口(不设置时允许所有类型格式的文件):
class Avatar < CarrierWave::Uploader::Base
    def extension_whitelist
        %w(jpg jpeg gif png)
    end
end

我们需要实现的功能

根据上面的分析,我们发现carrierwave已经帮我们实现了model层的内容,并为我们提供了重要的接口,我们需要做的就是实现view层和controller层的功能,使上传文件的整个系统能够正常运行。下面,我们就来完成整个系统。
上传文件系统开发流程

我们会用一个非常简单的demo来说明上传文件的流程,这个demo实现了上传文件的最基本的功能,虽然很简单,但每一步都是不可或缺的,缺少任何一个部分,都会导致上传文件的失败。

1. 安装carrierwave(Gemfile.rb

gem 'carrierwave', '>= 1.0.0.beta', '< 2.0'

2. 生成uploader(model层)

rails generate uploader Myuploader

3. 生成文件:app/uploaders/myuploader_uploader.rb,在这个文件中定义了存储文件位置等接口。现在我们需要在view层添加接口

<%= form_tag 'mystore', :multipart => true do%>
    <%= file_field_tag :localfile %>
    <%= submit_tag "上传"%>
<% end %>

我们在页面中添加一个最小的接口单元,form_tag 中添加 :multipart => true 即可实现文件上传,否则会导致无法上传。
第一行中 'mystore' 是指提交按钮发送请求(post)的url,这是一个相对路径,具体内容请复习表单内容。

4. 接着我们需要通过controller来连通整个系统。一方面,controller需要获得提交的http请求中包含的有效的文件信息,另一方面,需要将获得的信息送到model中进行处理。在rails的控制器文件中(*_controller.rb)添加:

def mystore
    up_file = params[:localfile]
    myfile = MyuploaderUploader.new
    myfile.store!(up_file)
end
注意,`submit_tag` 发送http请求时的方式是post,因此需要在 `routes.rb` 文件中添加post方法。

通过上述四个步骤,我们便已经成功地实现了本地文件上传rails服务器的功能。

如果需要修改文件存储的位置,文件属性等问题,在 app/uploaders/myuploader_uploader.rb 文件中修改对应的参数即可。

其中,给上传文件配置合理文件名是很重要的事情,这里采用根据文件内容进行MD5加密来给文件命名的方法:

def filename
    if original_filename
        @name ||= Digest::MD5.hexdigest(File.open(current_path, "rb") { |f| "#{f.read}" })
        "#{@name}.#{file.extension}"
    end
end

这样,整个系统最基本的功能便实现了,我们可以愉快地上传文件了。

总结

carrierwave着实让文件上传变得轻松且愉快,虽然写了这么多,但实际需要自己写的东西是十分少的。然而在自己刚开发时,走了很多弯路,总结起来还是太急功近利了,为了实现功能而实现功能,没有很好地理解文件上传流程,也没有从整体上把握,最终自己给自己挖下了很多坑。

这篇blog写了这么多,不仅仅是介绍carrierwave这个gem包的开发,更重要的是为自己总结gem包开发时的思路:把握整体,弄清系统框架,了解gem包实现的功能,明确gem提供的接口,注重细节,才能更有效地去做rails gem开发。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,888评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,677评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,386评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,726评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,729评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,337评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,902评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,807评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,349评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,439评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,567评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,242评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,933评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,420评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,531评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,995评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,585评论 2 359

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,302评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,702评论 18 139
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,120评论 4 61
  • 每天下班,坐一趟地铁,然后出来等接驳巴士,这趟环线的巴士原本一刻钟一班,结果坐的人不多,司机硬是把它开成了半个小时...
    凯德印象阅读 755评论 1 7
  • 阳台上,两大盆吊兰花正在极力施展开它们翠绿的叶子,每一片都写满着生机与活力。炎热的夏季,当你的目光稍稍停留...
    夏若兮阅读 479评论 0 0