不管是springboot項目齐饮,還是我們每天都在玩的luminus項目闸昨,做web項目免不了處理一些資源文件,本篇介紹原因和常用案例医瘫。
Ring(一定要了解下)
Ring是一個Clojure Web程序庫铜邮,有點類似與Java的servlet萨蚕。
公司目前用的都是用luminus 框架new出來的web項目颗搂,也是用Ring把我們的代碼編譯到一個java servlet運行的担猛,所以必須去了解(有中文版)。但是本文只說Resources
的使用丢氢。
Ring框架里對靜態(tài)資源的文檔介紹確實不多傅联,在靜態(tài)資源(Static Resources)
案例一:dev模式可以,打包后找不到文件
一些新手經(jīng)常會說:“我本地沒問題”疚察。是的蒸走,本地確實沒問題,所謂的沒問題的代碼是這樣的,
比如要拷貝resources
里的一個文件
(with-open [in (io/input-stream (io/resource "abc/file.dat"))] ;; resources/abc/file.dat
(io/copy in (io/file "/path/to/your/target/path")))
這段代碼本地是沒問題稍浆,但是以jar方式運行就會找不到文件载碌。
這要了解下打包干了啥猜嘱,有點深。
簡單說就是本地運行時確實這個路徑下有這個文件嫁艇。但是打成jar以后可以解壓看看朗伶,這個路徑不是你腦子的那個路徑了。
- 原因:jar本身就是一個文件步咪,不是一個目錄论皆,代碼中拼接的文件路徑變成了
xxx.jar!abc/file.dat
,這是一個偽地址,解壓下jar也可以看到猾漫,確實不是這樣的点晴,所以找不到路徑。
那怎么寫呢悯周?就是用io/resource
代替赤裸裸的路徑,下面這個函數(shù)接收一個路徑粒督,只要是項目根目錄下resouces
里的地址,就不會有問題禽翼。
(defn- reader-excel-template
"讀入模板"
[t-path]
(let [resource (io/resource t-path)
file-name (str (common-utils/uuid) ".xlsx")]
(with-open [in (io/input-stream resource)]
(io/copy in (io/file file-name)))
(io/as-file file-name)))
案例二:寫個接口把resources里的文件讀出來
比如我們resources
里有這些文件屠橄。
$ tree -c -L 3
.
├── doc
├── migrations
├── public
│ ├── img
│ │ ├── img_index.png
│ │ ├── img_index_icon.png
│ │ └── warning_clojure.png
└── sql
├── queries.sql
寫一個接口:
["/download"
{:get {:summary "downloads a file"
:swagger {:produces ["image/png"]}
:handler (fn [_]
{:status 200
:body (-> "public/img/warning_clojure.png"
(io/resource)
(io/input-stream))})}}]
上面這個接口,可以是resources
目錄里的任意文件闰挡,只是我們按照常規(guī)放在了public目錄里了锐墙。瀏覽器訪問這個接口時,像pdf长酗,圖片溪北,視頻等瀏覽器會直接打開,其他不支持的格式夺脾,會開始下載之拨。
如果格式明確,向讓調(diào)用者知道類型劳翰,可以在handler
指定類型,比如這個獲取圖片的敦锌,可以加:
:headers {"Content-Type" "image/png"}
案例三:把靜態(tài)文件讓jar服務(wù)帶起來
我們有個build好的前端項目馒疹,當(dāng)然包括了index.html,js,css,images等佳簸。想要讓jar啟動時,作為server同時把這個前端項目颖变,不需要使用nginx生均,python等。
- 前端代碼全部復(fù)制到
resources/public
目錄下腥刹。
可能如下:
$ pwd
/Users/mahaiqiang/git/redcreation/chutian/src/clj-backend/resources/public
$ ll
total 352
-rw-r--r-- 1 mahaiqiang staff 96K Sep 8 16:00 256x256.icns
-rw-r--r-- 1 mahaiqiang staff 1.7K Sep 8 16:00 32x32.icns
-rw-r--r-- 1 mahaiqiang staff 1.3K Sep 8 16:00 asset-manifest.json
-rw-r--r-- 1 mahaiqiang staff 5.7K Sep 8 16:00 electron.js
-rw-r--r-- 1 mahaiqiang staff 40K Sep 8 16:00 icon.png
-rw-r--r-- 1 mahaiqiang staff 536B Sep 8 16:00 index.html
-rw-r--r-- 1 mahaiqiang staff 532B Sep 8 16:00 log.js
-rw-r--r-- 1 mahaiqiang staff 284B Sep 8 16:00 manifest.json
-rw-r--r-- 1 mahaiqiang staff 525B Sep 8 16:00 preload.js
-rw-r--r-- 1 mahaiqiang staff 1.8K Sep 8 16:00 product.js
drwxr-xr-x 5 mahaiqiang staff 160B Sep 8 16:00 static
- 配置handler马胧,用
warp-resource
函數(shù)把靜態(tài)資源加進(jìn)來。
(:require [ring.middleware.resource :as resource])
(defn- async-aware-default-handler
([_] nil)
([_ respond _] (respond nil)))
;;靜態(tài)資源放在resources/public/目錄下.注意:index.html里引入的static前不能有斜線這個絕對路徑
(defn- create-resource-handler []
(resource/wrap-resource "/" {:root "resources/public"}))
(mount/defstate app-routes
:start
(ring/ring-handler
(ring/router
[(conj (base/service-routes)
(base/swagger-routes)
(api-public-routes)
(api-routes)
(api-callback-routes)
(admin-routes)
(admin-pulic-routes))])
(ring/routes
(create-resource-handler)
(wrap-content-type (wrap-webjars async-aware-default-handler))
(ring/create-default-handler))))
案例四:用jar代理本地指定目錄下的任意文件
我們可能把本地一個目錄當(dāng)成了文件服務(wù)器衔峰,上傳時上傳到這個目錄佩脊,上傳以后查看當(dāng)然也要能訪問到蛙粘。
Ring框架,也有靜態(tài)資源(Static Resources)威彰,寫了也不點進(jìn)去看出牧,搬過來吧.
Web應(yīng)用經(jīng)常需要服務(wù)靜態(tài)內(nèi)容,例如圖片或者樣式表.Ring提供了兩個中間件函數(shù)來做這個事情.
一個是wrap-file
.它服務(wù)來自當(dāng)前文件系統(tǒng)一個目錄的靜態(tài)內(nèi)容:
(use 'ring.middleware.file)
(def app
(wrap-file your-handler "/var/www/public"))
另一個是wrap-resource
.它服務(wù)來自JVM classpath下的靜態(tài)內(nèi)容:
(use 'ring.middleware.resource)
(def app
(wrap-resource your-handler "public"))
改造下我們的項目,
(:require [ring.middleware.resource :as resource]
[ring.middleware.webjars :refer [wrap-webjars]])
(mount/defstate app-routes
:start
(ring/ring-handler
(ring/router
[(conj (base/service-routes)
(base/swagger-routes)
(api-routes))])
(ring/routes
(wrap-file (ring/create-resource-handler {:path "/"})
"/Users/mahaiqiang/Downloads/") ;;把本地下載目錄當(dāng)成文件存儲服務(wù)器
(wrap-content-type (wrap-webjars (constantly nil)))
(ring/create-default-handler))))
案例三和案例四可以同時使用歇盼,不沖突舔痕。