下載檔案亂碼編碼問題


#1

請教railsfun大神們,我要link to 一個下載的vtt檔,打開後顯示亂碼,請問該如何設定呢?

我有在application.rb 設 config.encoding = “utf-8” 無效

在application.html.erb 設 <meta charset="utf-8"/> 無效

請問我有那邊設定不對?

再次感謝大神指導

code:


#2

順便改標題 … 我大概說明一下 … 什麼叫做『檔案編碼』 … ,以「橘子」來做解釋

任何字串都有編碼,類似 Big5(1 ~ 2 bytes 向下支援 ASCII 結果發生『許功蓋』問題) / UTF-8(1 ~ N bytes,向下支援 ASCII,類似 emoji 有複合位元,還有 RTL 的控制位元等等),這邊指的是橘子的『肉和籽』(下稱 body)也就是真實狀況,這邊可以用 Ruby 的 encode 做調整,但調整前應該要先調整正確的 meta

再來一個字串要以怎樣的編碼顯示,電腦收到的字串其實都是 binary,每個字串的 binary 會掛一個 meta data 來形容這個 binary 是什麼編碼,通常是 Big5 body + Big5 meta,UTF-8 body + UTF-8 meta,弄錯不一定會亂碼,因為類似都有向下支援 ASCII 之類的,而這邊的 meta 指的是橘子的『皮』,你可以換成蘋果皮榴槤皮隨便你哩,而 Ruby 下用 force_encoding 就可以換 meta ,換 meta 的過程不影響 body,encode 則是 meta + body 一起換,而用 encoding 來看目前是什麼 meta

最後就是你主要的問題了,你包裝袋上面寫了啥(下稱 charset) … response 的 http header 的 Content-Type 中可以放 charset ,裡面就可以塞你認為的編碼,沒寫的話別人就會亂猜 … 現在公約應該都是 UTF-8 就是,而很多這種包裝袋內的水果都把皮剝掉了 … 所以很多時候也可以把包裝袋當成水果的皮

所以以上,你的 vtt 檔真正的 body 編碼為何?,送出的 http header 的 charset 為何?,甚至 content_type 為何?封包是由 Rails / CDN / nginx / apache 哪個丟出?(很多時候沒皮的水果,包裝袋都是 http server 給的,甚至沒給裸裝就丟出去,沒設好接收方就只能亂猜了)

上述可以推成檢查項目:

  1. 你送的是橘子的包裝袋,橘子的皮,橘子的肉和籽,且對方知道那是橘子
  2. 對方懂得吃橘子(UTF-8 假設),說不定他喜歡吃流連(Big5 大家都討厭榴蓮,但有人很愛)

大概就這樣而已唄哈哈,最後,這個和你 application.html.erb<meta charset="utf-8"/> 完全無關,你去水果行搬了一箱上面貼著[?]的箱子,回家發覺東西不對,是水果行的問題還是原產地的問題?尤其水果行只是轉售而已,還叫你去隔壁領貨,沒拆開過且沒收你的錢,甚至你去過一次它就要搬家了 … 所以請從源頭農夫甚至種子那邊開始找唄


#3

謝謝您的詳細回覆,用「橘子」解釋有比較容易了解!我這就從源頭來處理


#4

感謝JC大神點化
發現瀏覽器的default是Big5,用chorme外掛


把它改成utf-8就正常了
只是感覺在應有更好的做法
大概就是vtt裸裝就丟出去的問題
若直接把vtt檔案拖到browser可以自動判定為utf-8
用但用rails link to就不會
這部份我還要再想一下


#5

沒 … 你還是沒看懂我的意思,你應該是 nginx 之類的直出然後沒出正確的 charset & Contet-Type,此時改用 Rails 來做 send_file 就可以驗證這件事情

你增加一個 controller 然後弄類似的語法

def download_me
  # path 為錯誤示範,請過濾到不能過濾才行,否則一定會被攻擊 ... 這邊單純的用 .to_i 來強制數字過濾
  send_file "#{Rails.root}/public/test#{params[:id].to_i}.vtt" , type: 'text/plain; charset=utf-8'
end

然後確定封包取得時, content-type == type 應該就會正常才是,所以下載網址會變成類似 /static/download_me/123,而不是直接的檔名哩,也同我上面的,你到底是誰出檔案回去?直接是檔名沒過水的話,應該就是前面的 http server 自己幫你丟的,不會過到 Rails 身上

這樣丟的缺點是還要經過 Rails 的 process 所以會佔掉 worker,且該 worker 還要等使用者下載完畢之後才會釋放(大概 20 個人同時下載你的 server 應該就不會動了),速度也會慢很多所以建議只用在測試和特殊用途上,如果上面測試成功了,其實去調整 Apache / Nginx 想辦法回正確的 Content-Type 即可(應該有全域和分 mime,深入點還能做到有副檔名綁定)就可以完成一樣的事情唄 … 類似


如果有改設定必須要重啟 Apache / Nginx 才會套用,不過這篇未測,要自己玩玩看測試就是,以上


#6

感謝JC!!! 晚輩恍然大悟,一會就來使用您的方法,並留意安全性問題,可以的話調Content-Type,再次大謝!