環境:Ubuntu 12.04 64bit + Ruby 1.9.3 + Rails 3 + MySQL。Windows 2003 客戶端上傳文件:
.gitconfig(ASCII 文件名)✅ 成功,content_type: application/octet-stream新浪微博數據挖掘.pdf(中文文件名)❌ 失敗,content_type: binary/octet-streamback.jpg(二進制圖片)❌ 失敗,content_type: binary/octet-stream報錯堆棧:
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'
對比發現,成功與失敗的關鍵差異在於 @content_type:
application/octet-stream → Rails 識別為文本流,按 UTF-8 處理。binary/octet-stream → Rails 識別為二進制流,但 Windows 下 File.open(path, 'w') 默認文本模式,會將 \r\n 轉換為 \n,導致二進制數據損壞,觸發編碼轉換異常。Ruby 1.9+ 強編碼機制下,二進制數據被誤判為 ASCII-8BIT,嘗試轉 UTF-8 失敗。
# 修改前
File.open(Rails.root.join('public','uploads',uploaded_io.original_filename), 'w') do |f|
f.write(uploaded_io.read)
end
# 修改後:'w' → 'wb' (b = binary)
File.open(Rails.root.join('public','uploads',uploaded_io.original_filename), 'wb') do |f|
f.write(uploaded_io.read)
end
wb 模式禁用行尾轉換,按原始字節寫入,完美解決圖片、PDF、中文文件名等所有二進制上傳問題。
/var/app/uploads),防止直接 URL 訪問腳本。rw-(無執行權限 x),防止惡意腳本執行。content_type)+ 文件魔數,防偽造擴展名/Content-Type。pdf,doc,xls;圖片場景只允許 jpg,png,gif,webp;嚴禁 exe,sh,php,jar 等可執行類型。Encoding::UndefinedConversionError 本質是 Windows 文本模式破壞二進制數據。將 File.open 模式從 'w' 改為 'wb' 即可徹底修復。結合路徑隔離、權限控制、類型白名單、異步處理,構建安全可靠的文件上傳體系。