Rails 4.2.x - 零大小的上传文件

时间:2015-11-06 05:11:08

标签: ruby-on-rails image file-upload

我在浏览中显示上传的图片时遇到问题。由于/public/uploads中每个上传文件的大小为零而导致IMO。

目前正试图掌握上传和提供文件没有任何ruby / rails gem

为此我运行了一个带有基本脚手架的原始应用程序,仅用于测试上传/保存周期:

schema.rb:

ActiveRecord::Schema.define(version: 20151104043709) do

  create_table "uploads", force: :cascade do |t|
    t.string   "filename"
    t.string   "content_type"
    t.binary   "content"
    t.datetime "created_at",   null: false
    t.datetime "updated_at",   null: false
  end

end

UploadsController#创建:

  def create
    @upload = Upload.new(upload_params)
    data = params[:upload][:file]

    File.open(Rails.root.join('public', 'uploads', data.original_filename), 'wb') do |f|
      f.write(data.read)
    end

    respond_to do |format|
      if @upload.save
        format.html { redirect_to @upload, notice: 'Upload was successfully created.' }
        format.json { render :show, status: :created, location: @upload }
      else
        format.html { render :new }
        format.json { render json: @upload.errors, status: :unprocessable_entity }
      end
    end
  end

模型Upload.rb:

class Upload < ActiveRecord::Base
    include ActionView::Helpers::NumberHelper
    attr_accessor :upload

    def file=(upload_data)
        self.filename = upload_data.original_filename
        self.content_type = upload_data.content_type
        self.content = upload_data.read
    end

    def filename=(new_filename)
        write_attribute("filename", sanitize_filename(new_filename))
    end

private

    def sanitize_filename(filename)
        just_filename = File.basename(filename)
        just_filename.gsub(/[^\w\.\-]/, '_')
    end
end

上传#show view:

<p id="notice"><%= notice %></p>

<p>
  <strong>Filename:</strong>
  <%= @upload.filename %>
</p>

<p>
  <strong>Content type:</strong>
  <%= @upload.content_type %>
</p>

<p>
  <%= image_tag "/public/uploads/#{@upload.filename}" %>
</p>

<%= link_to 'Edit', edit_upload_path(@upload) %> |
<%= link_to 'Back', uploads_path %> | 
<%= link_to 'Download', download_path(id: @upload) %>

问题:不太好的是所有文件都在/public/uploads目录中获得0个字节。

并查看这样的渲染: enter image description here

索引视图: enter image description here

并且无法加载图片示例: enter image description here

并证明所有文件都是零字节: enter image description here

注意:二进制文件应该可以正常工作,因为可以使用send_data对每个文件进行dwnl:

  def download
    send_data(@upload.content,
              filename: @upload.filename,
              content_type: @upload.content_type,
              )
  end

+ enter image description here

=========================================== 问题:应用程序的不正确以及如何修复它以在视图中显示图像或图像?在此先感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

试试这个,我认为这应该会有所帮助。

original_filename =  data.original_filename.to_s
tmp = data.tempfile
file = File.join("public/uploads", original_filename)
FileUtils.cp tmp.path, file

答案 1 :(得分:1)

看起来你正在混淆两种不同的存储文件的方法:

  1. 在数据库中存储为二进制Blob(例如:数据库中的content列)
  2. 将文件保存到文件系统(例如:/public/uploads/
  3. 您的实施有两点:您必须在content表的uploads列中将文件保存为二进制文件,并且您正在保存 到文件系统的东西。你需要挑选一个或另一个。

    如果你想使用方法2(文件系统),那么它将需要更多的工作才能实现。 bit 。如果这是你真正想做的事情,请告诉我,我可以用解决方案编辑我的答案。

    但是,您已经使用方法1(数据库)了。我们只需要以稍微不同的方式将各个部分组合在一起。

    方法1:在数据库中存储二进制Blob

    您提到send_data正在为您工作 - 这正是我们将要用来解决您的问题的方法。

    首先,向UploadsController添加方法

    def show_image
      @upload = Upload.find(params[:id])
      send_data @upload.content, type: @upload.content_type, disposition: 'inline'
    end
    

    接下来,添加相应的路线到routes.rb

    get 'show_image', to: 'uploads#show_image'
    

    最后,在您看来,/uploads/show.html.erb,将image_tag替换为此<%= image_tag url_for(controller: "uploads", action: "show_image", id: @upload.id) %>

    send_data

    然后我们走了。这应该足以让它发挥作用。

    我们基本上定义了一个控制器操作来从数据库中获取二进制blob,然后使用带有'inline'处置的link_tag将文件返回到具有正确MIME类型的视图,以便{您视图中的{1}}知道如何处理它。

    您会注意到,您可以删除create UploadsController方法中的几乎所有内容。您只需要标准的样板代码:

    def create
      @upload = Upload.new(upload_params) # <-- All you need
    
      respond_to do |format|
      ...
    end
    

    您也可以删除/public/uploads目录,因为您没有使用它。该文件使用此方法完全存储在数据库中,不会出现在您的文件系统中。

    希望这有用!如果这对您有用,请告诉我。

    注意方法2(文件系统)

    我猜你这样做是为了学习体验,这很棒,但如果你曾尝试将上传的文件存储在文件系统中,那就是&#34;真实&#34 ;应用程序,你真的应该使用gem来处理它。 Paperclip使事情变得非常简单,即使在最终将文件存储在S3等其他地方的生产应用程序中也是一个非常有用的工具,因此学习它绝对不浪费。

    方法2 - 在文件系统中存储文件

    好的,事实证明你的文件系统工作代码实际上非常接近工作。主要问题是在file=(upload_data)的{​​{1}}方法中,您调用了upload.rb。然后,在upload_data.read中,您拨打UploadsController#create。这里需要注意的重要一点是f.write(data.read)data是完全相同的文件流。为什么这很重要?

    好吧,关于Ruby中的upload_data(以及大多数其他文件读取方法)的事情是隐藏的可变状态跟踪当前&#34;位置&#34;在文件流的内容中。 (这很有用,因为大多数文件读取都是批量完成的,例如read一次读取一行文件。)那么foreach 真正做的是它& #34;读取文件的其余部分&#34;。如果你的位置&#34;是在开头,然后&#34;文件的其余部分&#34;是整个文件。但是,如果你已经过了一半,那么&#34;其余的&#34;只是下半场。因此,&#34;当前位置&#34;当文件完成file.read时,它总是在文件的最后结束,这意味着(除非我们&#34;倒带&#34;)将来任何对read的调用都会返回空字符串,因为没有更多&#34;文件的其余部分&#34;。例如:

    read

    (好的,如果你想在阅读答案之前试一试,那么你有足够的信息可以解决问题。如果没有,或者当你准备好了,请继续阅读...... )

    <强>解决方案

    myfile = File.open(some_path_to_file, 'r') myfile.read # => "lorem ipsum..." # (file contents from beginning) myfile.read # => "" # (empty string) myfile.rewind # => 0 (resets 'position' to position 0, the beginning) myfile.read # => "lorem ipsum..." # (file contents from beginning) 中,进行以下更改文件=(upload_data)

    upload.rb

    显然,在真实的应用中,我们会删除def file=(upload_data) self.filename = upload_data.original_filename self.content_type = upload_data.content_type self.content = upload_data.read # <-- Either Delete this line, OR upload_data.rewind # <-- Add this line end 行,因为我们正在尝试使用文件系统来实现方法2,但由于这是一个测试应用,您可以完全拥有两者同时工作。要做到这一点,只需在最后添加self.content行。

    所以现在upload_data.rewind方法应该成功地将文件添加到create目录(它们不再是空的),所以请检查以确保它正常工作。接下来,我们必须确保正确设置视图。

    /public/uploads中,图片标记应如下所示

    show.rb

    您有<%= image_tag "/uploads/#{@upload.filename}" %> 而不是/public/uploads/...。出于安全原因,/uploads/...只能查看image_tag内部,因此您无需指定&#39; / public&#39;在路径中 - 它将自动添加。

    而且,我们已经完成了!请让我知道这对你有没有用。

    更多

    你可能应该做更多的事情。比如稍微修改/public操作:

    create

    此外,不像我们那样对路径进行硬编码,而是将数据库中的路径存储在File.open(Rails.root.join('public', 'uploads', data.original_filename), 'wb') do |f| f.write(data.read) f.close # <-- add this line. You should always explicitly close file streams after you open them. end #### something else here 列中,然后我们就可以将file_path传递给@upload.file_path

    image_tag

    然后确保我们的rails g migration AddFilePathToUploads file_path:string rake db:migrate 操作在保存之前手动设置路径。因此,请插入以下内容而不是&#34;其他内容&#34;

    create

    然后更改@upload.file_path = ['/uploads', data.original_filename].join '/'

    中的图片标记
    show.rb

    好的,现在我们已经完成了。我只想再次提出建议,查看Paperclip或CarrierWave宝石,因为它们可能会用于任何真实项目。

    我希望你学到了一些东西,这对你很有帮助。我也玩得很开心。当你让它工作时,让我知道!

    干杯