我在浏览中显示上传的图片时遇到问题。由于/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个字节。
注意:二进制文件应该可以正常工作,因为可以使用send_data
对每个文件进行dwnl:
def download
send_data(@upload.content,
filename: @upload.filename,
content_type: @upload.content_type,
)
end
=========================================== 问题:应用程序的不正确以及如何修复它以在视图中显示图像或图像?在此先感谢您的帮助!
答案 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)
看起来你正在混淆两种不同的存储文件的方法:
content
列)/public/uploads/
)您的实施有两点:您必须在content
表的uploads
列中将文件保存为二进制文件,并且您正在保存 到文件系统的东西。你需要挑选一个或另一个。
如果你想使用方法2(文件系统),那么它将需要更多的工作才能实现。 bit 。如果这是你真正想做的事情,请告诉我,我可以用解决方案编辑我的答案。
但是,您已经使用方法1(数据库)了。我们只需要以稍微不同的方式将各个部分组合在一起。
您提到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等其他地方的生产应用程序中也是一个非常有用的工具,因此学习它绝对不浪费。
好的,事实证明你的文件系统工作代码实际上非常接近工作。主要问题是在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宝石,因为它们可能会用于任何真实项目。
我希望你学到了一些东西,这对你很有帮助。我也玩得很开心。当你让它工作时,让我知道!
干杯