不管是springboot项目,还是我们每天都在玩的luminus项目,做web项目免不了处理一些资源文件,本篇介绍原因和常用案例。
Ring(一定要了解下)
Ring是一个Clojure Web程序库,有点类似与Java的servlet。
公司目前用的都是用luminus 框架new出来的web项目,也是用Ring把我们的代码编译到一个java servlet运行的,所以必须去了解(有中文版)。但是本文只说Resources
的使用。
Ring框架里对静态资源的文档介绍确实不多,在静态资源(Static Resources)
案例一:dev模式可以,打包后找不到文件
一些新手经常会说:“我本地没问题”。是的,本地确实没问题,所谓的没问题的代码是这样的,
比如要拷贝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
代替赤裸裸的路径,下面这个函数接收一个路径,只要是项目根目录下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
目录里的任意文件,只是我们按照常规放在了public目录里了。浏览器访问这个接口时,像pdf,图片,视频等浏览器会直接打开,其他不支持的格式,会开始下载。
如果格式明确,向让调用者知道类型,可以在handler
指定类型,比如这个获取图片的,可以加:
:headers {"Content-Type" "image/png"}
案例三:把静态文件让jar服务带起来
我们有个build好的前端项目,当然包括了index.html,js,css,images等。想要让jar启动时,作为server同时把这个前端项目,不需要使用nginx,python等。
- 前端代码全部复制到
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
函数把静态资源加进来。
(:require [ring.middleware.resource :as resource])
(defn- async-aware-default-handler
([_] nil)
([_ respond _] (respond nil)))
;;静态资源放在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代理本地指定目录下的任意文件
我们可能把本地一个目录当成了文件服务器,上传时上传到这个目录,上传以后查看当然也要能访问到。
Ring框架,也有静态资源(Static Resources),写了也不点进去看,搬过来吧.
Web应用经常需要服务静态内容,例如图片或者样式表.Ring提供了两个中间件函数来做这个事情.
一个是wrap-file
.它服务来自当前文件系统一个目录的静态内容:
(use 'ring.middleware.file)
(def app
(wrap-file your-handler "/var/www/public"))
另一个是wrap-resource
.它服务来自JVM classpath下的静态内容:
(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/") ;;把本地下载目录当成文件存储服务器
(wrap-content-type (wrap-webjars (constantly nil)))
(ring/create-default-handler))))
案例三和案例四可以同时使用,不冲突。