背景:
新上線的一個需求,大家都做得比較辛苦。
同時,也收到使用者回報功能功能不好用[石化]
問題描述:
回報的問題是,在導出的檔案名看不懂,一旦導出多了,不好找。如果下載下傳一個使用者再自己重新命名一下,又會影響效率。
根據檔案名不知道裡面的内容
這就很煩了,
不改下名,不好找導出的檔案。
改吧,又太麻煩。
使用者又能拿這個功能怎麼樣,隻能吐槽這個新功能不好用罷了
這是一個非功能的體驗問題。
直接原因:
沒有指定下載下傳檔案名,浏覽器使用了預設的命名政策:将url上的非法字元去掉,然後拼一下。如果得到的字元串太長,還會進行截斷處理。
原因分析:
使用者執行導出後,後端傳回的是一個包含了導出内容的oss位址,也就是一個Url。前端直接把這個url放到<a>标簽中。使用者點選進行下載下傳
下載下傳時的互動
這種情況下,浏覽器下載下傳時展示在狀态欄上的名字,浏覽器就自由發揮了,目前浏覽器的命名規則是将url上的非法字元去掉,然後拼一下。
下載下傳的檔案名
優化意見:
方案1:由伺服器寫入資料流的方式下載下傳,同時由伺服器指定一個自定義的檔案名。
方案2:伺服器傳回存放業務資料的oss位址,前端指定一個自定義的檔案名。
确定優化方案
最終標明了方案2。
原因是方案2改動最小,并且可以避免下載下傳時導緻業務資料缺失的問題。
客戶都是用chrome,也規避了方案2的浏覽器相容性問題。
技術方案對比
方案1:
伺服器傳回資料流的方式
https://www.processon.com/view/6363c4d2e0b34d77dbcd4919
優點:
- 可以由後端靈活自定義浏覽器下載下傳時的檔案名。沒有相容性問題
- 代碼實作簡單。代碼量少,實作簡單
缺點:
- 資料導出過程中如果出現異常,會出現隻導出一部分資料的情況,整個下載下傳過程并不會完全中斷。使用者沒有辦法分辨是否下載下傳完成。
- 伺服器帶寬打滿後會影響其它功能的使用。伺服器寫資料到浏覽器會占用伺服器網卡的總帶寬,如果打滿,其它功能也用不了。可以把帶寬想象成一座橋,大檔案就像一個大卡車。
- 影響到伺服器的穩定性。大檔案生成及傳輸過程會持續占用伺服器記憶體。伺服器的記憶體是有限的,下載下傳大檔案的功能占用了,其它功能就不能正常工作了。
- 分布式環境中,增加了代碼的複雜度。Feign或RestTempate在處理位元組流時需要特殊的配置,在更新這些http客戶元件時,也需要驗證對這些已有功能的影響。
方案2:
使用oss作為資料中轉站
https://www.processon.com/view/6363c4d2e0b34d77dbcd4919
優點:
- 資料不會丢失。資料上傳oss時報錯時,整個導出就報錯了,不會出現使用者隻拿到部分資料的情況
- 不會占用伺服器出口帶寬。傳回給前端的一個url,不管導出多大的檔案,出口帶寬都不會受到影響。
缺點:
1.相容性問題。
在 HTML5 中,download 屬性是 <a> 标簽的新屬性。
相容性
定義和用法<a download="filename">filename 規定作為檔案名來使用的文本。
該屬性也可以設定一個值來規定下載下傳檔案的名稱。所允許的值沒有限制,浏覽器将自動檢測正确的檔案擴充名并添加到檔案 (.img,.xls,.doc,.pdf, .txt, .html, 等等)。
在 <a> 标簽中必須設定 href 屬性。
2.需要下載下傳的資源是同源的
同源政策(Same origin policy)是基于安全的考慮,是浏覽器阻止從一個源加載的文檔或腳本擷取或設定另一個源加載額文檔的屬性,是浏覽器對JavaScript施加的安全限制,是浏覽器最核心也最基本的安全功能。簡單地說隻要在浏覽器裡打開頁面,就預設遵守同源政策,目的是為了保證使用者的隐私安全和資料安全。
那麼什麼是同源呢?
所謂同源是指兩個 URL的"協定+域名+端口"三者都相同
技術方案的詳細設計
方案1:
按照http協定的要求,把業務資料轉換成資料流寫到HttpServletResponse
指定這個資料流的type:
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
指定檔案名:
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
show me the code
https://github.com/helloworldtang/spring-boot-cookbook/blob/master/learning-demo/src/main/java/com/tangcheng/learning/adapter/openapi/ExportController.java
方案2:
show me the code
https://github.com/helloworldtang/spring-boot-cookbook/blob/master/learning-demo/src/main/resources/templates/jsDownload.html
小結:
導出場景,伺服器直接傳回資料流到前端,要關注占帶寬的情況。
如果是由前端指定下載下傳的下載下傳名,需要考慮相容性問題。
最佳方案:
伺服器傳回oss url且指定自定義檔案名。
注意事項:要解決檔案名相同時會互相覆寫的問題。需要在url上對相同檔案名的oss檔案在path是進行區分。比如url是這種格式 ip/userId/yyyy/MM/dd/hh/mm/ss/SSS/一個随機數/自定義檔案名