服務器用的是ubuntu12 64bit,環境是ruby1.9.3+rails3+mysql,測試是在windows2003上。
上傳一個【.gitconfig】文件,沒有問題,上傳【新浪微博數據挖掘.pdf】報錯,上傳【back.jpg】報錯。
下面是兩段信息,是從【log/production.log】中粘貼出來的。上面一段你是沒有問題的日志,下面一段是報錯之后的日志。
?
- Started?POST?"/posts"?for?106.3.102.43?at?2012-10-29?21:16:26?+0800?
- Processing?by?PostsController#create?as?HTML?
- ??Parameters:?{"utf8"=>"",?"authenticity_token"=>"QG8aU6/VW5ZMagzyGhjdbm7fSzr4MB5CKdJeGBIeOa4=",?"post"=>{"category_id"=>"1",?"title"=>"666666666666",?"url"=>"6666666",?"picture"=>#<ActionDispatch::Http::UploadedFile:0x000000032fb838?@original_filename=".gitconfig",?@content_type="application/octet-stream",?@headers="Content-Disposition:?form-data;?name=\"post[picture]\";?filename=\".gitconfig\"\r\nContent-Type:?application/octet-stream\r\n",?@tempfile=#<File:/tmp/RackMultipart20121029-2609-1lrmc9o>>,?"content"=>"6666",?"tags_attributes"=>{"0"=>{"title"=>""}}},?"commit"=>"Create?Post"}?
- Redirected?to?http://42.121.5.68/posts?
- Completed?302?Found?in?36ms?(ActiveRecord:?30.1ms)?
?
- Started?POST?"/posts"?for?123.114.36.100?at?2012-10-30?08:58:13?+0800?
- Processing?by?PostsController#create?as?HTML?
- ??Parameters:?{"utf8"=>"",?"authenticity_token"=>"rRnhcDWYDn+OntxxC2LmIEHpSpjWI5glrs6JlprG1Ho=",?"post"=>{"category_id"=>"1",?"title"=>"博客嘗試最新法寶",?"url"=>"post7",?"picture"=>#<ActionDispatch::Http::UploadedFile:0x000000030df9a0?@original_filename="新浪微博數據挖掘方案.pdf",?@content_type="binary/octet-stream",?@headers="Content-Disposition:?form-data;?name=\"post[picture]\";?filename=\"\xE6\x96\xB0\xE6\xB5\xAA\xE5\xBE\xAE\xE5\x8D\x9A\xE6\x95\xB0\xE6\x8D\xAE\xE6\x8C\x96\xE6\x8E\x98\xE6\x96\xB9\xE6\xA1\x88.pdf\"\r\nContent-Type:?binary/octet-stream\r\n",?@tempfile=#<File:/tmp/RackMultipart20121030-16129-15agvlb>>,?"content"=>"博客嘗>試最新法寶",?"tags_attributes"=>{"0"=>{"title"=>"博客嘗試最新法寶"}}},?"commit"=>"Create?Post"}?
- Completed?500?Internal?Server?Error?in?45ms?
- ?
- Encoding::UndefinedConversionError?("\xE2"?from?ASCII-8BIT?to?UTF-8):?
- ??app/controllers/posts_controller.rb:60:in?`write'?
- ??app/controllers/posts_controller.rb:60:in?`block?(2?levels)?in?create'?
- ??app/controllers/posts_controller.rb:59:in?`open'?
- ??app/controllers/posts_controller.rb:59:in?`block?in?create'?
- ??app/controllers/posts_controller.rb:56:in?`create'?
比較之后,發現兩段的picture部分的@content_type不一樣,成功的是@content_type="application/octet-stream",失敗的是@content_type="binary/octet-stream"。
嘗試上傳一個txt文件,成功了,@content_type部分是@content_type="text/plain"。
確定是這個部分的原因,也就是編碼,所以報錯編碼錯誤,未定義編碼轉換。
上傳部分的代碼如下
?
- uploaded_io?=?params[:post][:picture]?
- ????????if?uploaded_io?!=?nil?and?uploaded_io.content_type.match('image')?
- ?
- ??????????File.open(Rails.root.join('public','uploads',uploaded_io.original_filename),'w')?do?|f|?
- ?
- ????????????f.write(uploaded_io.read)?
- ??????????end?
- ?
- ????????else?
- end?
經過一番查找,找到了深層的原因,原來是二進制文件的緣故,ruby在讀取和保存的時候會自動處理二進制文件,不需要特殊的方式。
可是在windows中,二進制和文本文件是不同的,在二進制mode下,結束行不能被轉義為一個單獨的換行,而是被保存為一個回車和一個換行。所以如果讀取的是二進制文件,需要在open的時候要指明讀取的是二進制文件wb。b就是二進制的意思。
圖片默認按照二進制文件處理,所以就中招了。其實也只需要把w改成wb就可以了。
?
下面就沒有這個問題了。
- uploaded_io?=?params[:post][:picture]?
- ????????if?uploaded_io?!=?nil?and?uploaded_io.content_type.match('image')?
- ?
- ??????????File.open(Rails.root.join('public','uploads',uploaded_io.original_filename),'wb')?do?|f|?
- ?
- ????????????f.write(uploaded_io.read)?
- ??????????end?
- ?
- ????????else?
- end
?
?
- <div?class="field">?
- ??<%=?f.label?:picture?%><br/>?
- ??<%=?f.file_field?:picture?%>?
- </div>?
從安全角度來講,文件上傳要嚴格控制路徑,權限,以及上傳的類型。
?
- 路徑,就是文件在服務器的保存路徑,最好是單獨路徑,不要放在根目錄,規劃好文件夾,還要做好重命名,因為上傳的人不知道服務器上面是不是存在同名的文件,這就涉及文件實際的名稱和用戶需要看到的文件名的映射。
?
- 權限,就是上傳路徑給用戶的權限,最好不要有執行權限,只有讀寫權限就可以了。有必要的話,需要劃分用戶文件夾來區分權限。保留一個公共文件夾。等等,根據情況而定。
?
- 上傳的類型,上傳文件肯定和一個場景相關,一個場景可能只需要一類文件,比如文檔場景,圖片場景。最好在不同的場景控制不同的文件類型。執行文件要嚴格控制。
?
本文轉自 virusswb 51CTO博客,原文鏈接:http://blog.51cto.com/virusswb/1042740,如需轉載請自行聯系原作者