ERR_CONTENT_DECODING_FAILED, netty下載文件的壓縮編碼問題

說明: 本文涉及的netty源碼都是netty4.1

問題描述

我在將netty整合到我自己依賴注入框架Yao中來作爲web服務提供類似springmvc的能力的時候。先從官方demo開始。 整合Netty官方Demo HttpStaticFileServer的時候因爲在channel中加入了HttpContentCompressor導致下載文件不成功。

表現的現象是瀏覽器下載狀態碼是200. 但是文件下載的內容就會失敗,同時瀏覽器控制檯顯示ERR_CONTENT_DECODING_FAILED, 但是使用https協議的時候竟然可以下載。 並且服務端不會打印任何錯誤信息。

思考

因爲控制檯不打印錯誤,我又沒有注意到瀏覽器控制檯的ERR_CONTENT_DECODING_FAILED信息,但是官方的demo是正常的,我整合的就會有異常。所以我懷疑是我哪裏加入了錯誤的handler。於是我一個個註釋handler. 先從我自己的handler開始,最後終於確定了元兇HttpContentCompressor。 我去掉HttpContentCompressor就一切正常。

由此確定是Http協議的壓縮編碼問題,查看請求頭髮現請求頭中Accept-Encoding字段正常傳入值了,但是下載文件的時候壓縮編碼異常了。

此時我搜百度,發現這篇 https://www.sohu.com/a/190735418_684743文章。 終於找到原因了。

原因

HttpContentCompressor繼承了HttpContentEncoder類。

從圖中可以看到這個類只是處理HttpObject和HttpRequest類型的數據的壓縮編碼,而我們的demo中HttpStaticFileServerHandler中io.netty.example.http.file.HttpStaticFileServerHandler#channelRead0方法,
在這裏插入圖片描述
從圖中可以看到, 它對於http和https協議使用了不同的方式下載文件,

  • 當使用https協議的時候往ctx中write的是HttpChunkedInput對象。
  • 使用http協議的時候往ctx中write中是DefaultFileRegion對象

正式由於這兩種不同的方式導致http的方式下載文件失敗。在下載文件的時候,response是分段發送的,

  1. 首先HttpHeader(實現了HttpObject)會先寫入ctx, 到達HttpContentCompressor的時候, 會在響應頭中加上content-encoding: gzip 。 源碼在: io/netty/example/http/file/HttpStaticFileServerHandler.java:191
  2. 再次寫入響應體的時候是DefaultFileRegion對象, DefaultFileRegion並沒有繼承HttpObject, 所以經過HttpContentCompressor的時候直接跳過,並不會進行壓縮.
  3. 最後是LastHttpContent, 這個是空白的內容
    收到的響應請求頭中content-encoding: gzip告訴瀏覽器響應是gzip壓縮的,所以瀏覽器就會以gzip的方式去解壓響應體, 解壓失敗瀏覽器控制檯輸出ERR_CONTENT_DECODING_FAILED

而HttpChunkedInput實現了httpObject接口,所以他結果HttpContentCompressor的時候正常壓縮了,所以沒有問題。

解決辦法

1. 全部使用HttpChunkedInput下載文件

由於gzip壓縮可以有效節約寬帶, 所以我全部使用HttpChunkedInput的方式下載文件。

2. 去掉HttpContentCompressor壓縮編碼

去掉編碼是最快捷的解決方式, 所有的http請求都不進行壓縮

總結

  1. 通過HttpChunkedInput + HttpContentCompressor,可以實現壓縮文件傳輸。當然,也可以自定義一個FileRegionCompressorHandler,根據客戶端請求的Accept-Encoding,實現對文件內容的壓縮,壓縮之後,調用HttpServerResponseEncoder,對響應內容進行編碼
  2. DefaultFileRegion實現了零拷貝的方式, 可以高效的傳輸文件
  3. 排查問題的時候, 不要忽略任何一個信息, 比如瀏覽器控制檯的日誌等, 要跟進源碼查看
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章