November 6, 2019
By: 马海强

excel导出和下载demo

  1. 后端生成文件
  2. cljs前端实现(没有处理IE的情况)

本篇demo常用的excel导出功能的服务端生成文件,以及前端下载 后端给response写入stream。 前端用<a>标签模拟点击下载,是clojurescript和javascript交互的一个实例场景。

后端生成文件

使用的第三方库:dk.ative/docjure,创建一个excel还是很简单的。 贴个db查询后对数据处理,并利用该库生成一个excel的代码。(使用模板导出的情况后面再补)。

  • project.clj中引入上面的库
  • namespace里引入需要的函数
[dk.ative.docjure.spreadsheet :as ds :refer [create-workbook select-sheet row-seq set-row-style! create-cell-style! save-workbook! add-sheet!]]
  • 查询db,生成excel
(def patient-export-header
  ["编号" "姓名" "性别" "出生日期" "身高(cm)" "体重(kg)" "电话" "家庭住址"])

(defn rows-data [records]
  (cons patient-export-header records ))

(defn export-patient
  "export all patients in this office"
  [keywords office-id]
  (let [data-source (db-pat/export-patients {:keywords keywords
                                             :office-id office-id})
        funcs [:patient-no
               :name
               :gender
               :birth
               :height
               :weight
               :mobile
               :address]
        records (map (apply juxt funcs) data-source)]
    (if (pos? (count records))
      {:status 200
       :headers {"Content-Type" "application/octet-stream;charset=UTF-8"
                 "Content-Disposition" (str "attachment;filename=" (rand-int 100) ".xlsx")}
       :body (let [wb (create-workbook "user-export"
                                       (rows-data records))
                   sheet (select-sheet "用户数据" wb)
                   header-row (first (row-seq sheet))]
               (set-row-style! header-row (create-cell-style! wb {:background :yellow,
                                                                  :font {:bold true}}))
               (.setDefaultColumnWidth sheet 12) ;;设置默认列宽
               (.setColumnWidth sheet 0 (* 20 256)) ;;设置第1列的列宽为20个字符
               (.setColumnWidth sheet 7 (* 50 256)) ;;设置第8列的列宽为50个字符
               (let [out (java.io.ByteArrayOutputStream.)]
                 (ds/save-workbook-into-stream! out wb)
                 (io/input-stream (.toByteArray out)))
               )}
      {:status 400
       :body {:message "没有可以导出的数据"}})
    ))

这样用浏览直接访问一个调用上面函数的接口就可以开始一个下载任务。

cljs前端实现(没有处理IE的情况)

参考了https://github.com/JulianBirch/cljs-ajax/blob/master/docs/formats.mdhttps://vimsky.com/article/4171.html

思录:在页面上创建一个a标签,模拟点击,然后再删除该标签。

(require '[re-frame.core :as re-frame])
(require '[ajax.core :as ajax])
(require '[ajax.protocols :refer [-body]])
(require '[reagent.core :as r])

(defn download [blob]
  (let [a (.createElement js/document "a")
        url (.createObjectURL js/URL blob)
        clean-up-f (fn []
                     (. (. js/document -body) removeChild a)
                     (.revokeObjectURL js/URL (. a -href)))]
    (. (. js/document -body) appendChild a)
    (set! (. a -style)  "disploy:none")
    (set! (. a -href) url)
    (set! (. a -download) "export.xlsx")
    (.click a)
    (js/setTimeout clean-up-f 1000)))

;;; export-data-ajax
(re-frame/reg-event-fx
 ::export
 (fn [cofx [_ _]]
   {:http-xhrio {:uri  "http://182.61.51.177:3055/api/guestbooks/files/export/excel"
                 :method :get
                 :timeout 10000
                 :response-format {:content-type "application/octet-stream"
                                   :description "export-excel"
                                   :type :blob
                                   :read -body}
                 :on-success [::start-download]}}))

(re-frame/reg-event-fx
 ::start-download
 (fn [cofx [_ response]]
   (let [file (js/Blob. (clj->js [response])
                        (clj->js {:type "application/octet-stream"}))]
     (download file))
   {:db (:db cofx)}
   ))

触发

(defn excel-export []
  (r/create-class
    {:reagent-render
     (fn []
       [:a {:on-click #(re-frame/dispatch [::export])} "导出试试"])}))

核心函数

(defn download [blob]
  (let [a (.createElement js/document "a")
        url (.createObjectURL js/URL blob)
        clean-up-f (fn []
                     (. (. js/document -body) removeChild a)
                     (.revokeObjectURL js/URL (. a -href)))]
    (. (. js/document -body) appendChild a)
    (set! (. a -style)  "disploy:none")
    (set! (. a -href) url)
    (set! (. a -download) "attachment.xlsx")
    (.click a)
    (js/setTimeout clean-up-f 1000)))
#_(download (new js/Blob ))    ;;放开注释就会导出

###后端参考

https://github.com/mjul/docjure/blob/master/test/dk/ative/docjure/spreadsheet_test.clj

Tags: clojure clojurescript