[英]Rails 4.2.x - zero size uploaded files
我在視圖中顯示上傳的圖像時遇到問題。 IMO由於/public/uploads
每個上傳文件的大小為零。
當前試圖在沒有任何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
上傳#顯示視圖:
<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個字節。
注意:二進制應能正常工作,因為它可以dwnl每個文件send_data
:
def download
send_data(@upload.content,
filename: @upload.filename,
content_type: @upload.content_type,
)
end
========================================= 問題:什么不適合應用程序以及如何修復它以顯示圖像或視圖中的圖像? 在此先感謝您的幫助!
試試這個,我認為這應該有所幫助。
original_filename = data.original_filename.to_s
tmp = data.tempfile
file = File.join("public/uploads", original_filename)
FileUtils.cp tmp.path, file
看起來您正在混淆兩種存儲文件的方法:
content
列) /public/uploads/
) 您的實現實現了兩者的全部功能:您肯定要在uploads
表的content
列中將文件另存為二進制文件,並將某些內容保存到文件系統中。 您需要選擇一個。
如果你想使用方法2(文件系統),它會采取更多的工作來得到它去。 如果這是您真正想要做的,請告訴我,然后我可以使用解決方案編輯答案。
但是, 方法1(數據庫)已經足夠了。 我們只需要以稍微不同的方式將各個部分放在一起。
您提到send_data
對您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) %>
然后我們走了。 這足以使其正常工作。
我們基本上定義了一個控制器動作來從數據庫中獲取二進制blob,然后使用具有'inline'
配置的send_data
將文件以正確的MIME類型返回到視圖,以便視圖中的link_tag
知道如何處理它。
您會注意到,您可以在UploadsController
的create
方法中刪除幾乎所有內容。 您需要的只是標准樣板代碼:
def create
@upload = Upload.new(upload_params) # <-- All you need
respond_to do |format|
...
end
您也可以刪除/public/uploads
目錄,因為您沒有使用它。 該文件通過這種方法完全存儲在數據庫中,並且不會出現在文件系統中。
希望這對您有所幫助! 讓我知道這是否適合您。
關於方法2(文件系統)的注意事項:
我猜您是作為學習體驗來做的,這很棒,但是,如果您試圖將上傳的文件存儲在“真實”應用程序的文件系統中,則確實應該使用gem來處理它。 回形針使事情變得非常簡單,即使在最終將文件存儲在S3等其他地方的生產應用程序中,它也是一個有用的工具,因此學習它絕對不是浪費。
好的,事實證明,文件系統工作的代碼實際上已經接近工作了。 主要問題在於,在upload.rb
的file=(upload_data)
方法中,您調用了upload_data.read
。 然后,在UploadsController#create
,調用f.write(data.read)
。 這里要注意的重要一點是data
和upload_data
是相同的確切文件流。 為什么這么重要?
嗯,差不多的東西read
Ruby中(以及大多數其他文件的閱讀方法)是存在隱患的是跟蹤在文件流的網頁內容的“位置”的可變狀態。 (這很有用,因為大多數文件讀取都是分批完成的,例如foreach
一次讀取一行。)所以file.read
真正 file.read
是它“讀取了文件的其余部分”。 如果您的“位置”在開頭,那么“文件的其余部分”就是整個文件。 但是,如果您走了一半,那么“其余”只是下半部分。 因此,“當前位置”在read
-ing時總是在文件的最后結束,這意味着(除非我們“后退”)任何以后的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.rb
,進行以下更改file =(upload_data)
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
顯然,在實際的應用程序中,我們將刪除self.content
行,因為我們正嘗試使用文件系統來實現方法2,但是由於這是一個測試應用程序,因此您可以完全同時工作。 為此,只需在末尾添加upload_data.rewind
行。
因此,現在create
方法應該可以將文件成功添加到您的/public/uploads
目錄(它們將不再為空),因此請檢查以確保工作正常。 接下來,我們只需要確保正確設置視圖即可。
在show.rb
,圖像標簽應如下所示
<%= image_tag "/uploads/#{@upload.filename}" %>
您使用/public/uploads/...
而不是/uploads/...
出於安全原因, image_tag
只能在/public
內部查看,因此您不必在路徑中指定'/ image_tag
它會自動添加。
並且,我們完成了! 請讓我知道這對你有沒有用。
更多
您可能還應該做幾件事。 像稍微修改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
然后確保我們的create
動作在保存之前手動設置路徑。 因此,請插入以下內容,而不是“此處還有其他內容”
@upload.file_path = ['/uploads', data.original_filename].join '/'
然后在show.rb
更改圖像標簽
<%= image_tag @upload.file_path %>
好了, 現在我們就大功告成了。 我只想再次提出建議,以檢出Paperclip或CarrierWave寶石,因為它們可能是您在任何實際項目中都會使用的寶石。
希望您學到了一些東西,對您有所幫助。 我也很開心。 讓我知道您何時開始工作!
干杯
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.