From 5057a18ae9dfb441b20a836ef6730f2b4151737d Mon Sep 17 00:00:00 2001 From: jlangch Date: Mon, 1 Apr 2024 11:23:42 +0200 Subject: [PATCH] emhanced :multipart module --- .../cheatsheet/section/ByteBufSection.java | 7 +- .../cheatsheet/section/PrimitivesSection.java | 5 + .../github/jlangch/venice/multipart.venice | 146 +++++++++++++----- 3 files changed, 114 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/ByteBufSection.java b/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/ByteBufSection.java index 71a520f99..e864b3d3f 100644 --- a/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/ByteBufSection.java +++ b/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/ByteBufSection.java @@ -44,10 +44,14 @@ public DocSection section() { bb_create.addItem(diBuilder.getDocItem("bytebuf")); bb_create.addItem(diBuilder.getDocItem("bytebuf-allocate")); bb_create.addItem(diBuilder.getDocItem("bytebuf-allocate-random")); - bb_create.addItem(diBuilder.getDocItem("bytebuf-from-string")); bb_create.addItem(diBuilder.getDocItem("bytebuf-byte-order!")); bb_create.addItem(diBuilder.getDocItem("bytebuf-byte-order")); + final DocSection string = new DocSection("String", "bytebuf.string"); + all.addSection(string); + string.addItem(diBuilder.getDocItem("bytebuf-from-string")); + string.addItem(diBuilder.getDocItem("bytebuf-to-string")); + final DocSection bb_test = new DocSection("Test", "bytebuf.test"); all.addSection(bb_test); bb_test.addItem(diBuilder.getDocItem("empty?")); @@ -59,7 +63,6 @@ public DocSection section() { bb_use.addItem(diBuilder.getDocItem("count")); bb_use.addItem(diBuilder.getDocItem("bytebuf-capacity")); bb_use.addItem(diBuilder.getDocItem("bytebuf-limit")); - bb_use.addItem(diBuilder.getDocItem("bytebuf-to-string")); bb_use.addItem(diBuilder.getDocItem("bytebuf-to-list")); bb_use.addItem(diBuilder.getDocItem("bytebuf-sub")); bb_use.addItem(diBuilder.getDocItem("bytebuf-pos")); diff --git a/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/PrimitivesSection.java b/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/PrimitivesSection.java index fb68b0cbf..d3fc061c2 100644 --- a/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/PrimitivesSection.java +++ b/src/main/java/com/github/jlangch/venice/impl/docgen/cheatsheet/section/PrimitivesSection.java @@ -211,6 +211,11 @@ public DocSection section() { hex.addItem(diBuilder.getDocItem("str/bytebuf-to-hex")); hex.addItem(diBuilder.getDocItem("str/format-bytebuf")); + final DocSection bytebuf = new DocSection("Bytebuf", "primitives.strings.bytebuf"); + strings.addSection(bytebuf); + bytebuf.addItem(diBuilder.getDocItem("bytebuf-from-string")); + bytebuf.addItem(diBuilder.getDocItem("bytebuf-to-string")); + final DocSection encode = new DocSection("Encode/Decode", "primitives.strings.encode"); strings.addSection(encode); encode.addItem(diBuilder.getDocItem("str/encode-base64")); diff --git a/src/main/resources/com/github/jlangch/venice/multipart.venice b/src/main/resources/com/github/jlangch/venice/multipart.venice index 161645834..3e492362d 100644 --- a/src/main/resources/com/github/jlangch/venice/multipart.venice +++ b/src/main/resources/com/github/jlangch/venice/multipart.venice @@ -33,7 +33,7 @@ (import :java.nio.file.Paths) -(defonce ^:private boundary-value (str (rand-bigint 256))) +(defonce ^:private boundary-value (str (rand-bigint 128))) (defonce ^:private dash "--") (defonce ^:private dq "\"") @@ -44,64 +44,126 @@ (defonce ^:private content-type "Content-Type: ") -(defn to-bytebuf [data] +(defn + ^{ :arglists '( + "(render parts)" ) + :doc + """ + Renders a map of named parts as *multipart* binary data. + + The part name must be a string and the part data may be of type: + * string + * string ("file:/user/foo/image.png" to reference a file) + * map (describing a part as :name, :mimetype, and :data) + * io/file + * all other part data types are converted with `(str data)` to a string + + Returns a bytebuf. + """ + :examples '( + """ + (do + (load-module :multipart ['multipart :as 'm]) + + (->> (m/render { "Part-1" "xxxxxxxxxxx" + "Part-2" "yyyyyyyyyyy"}) + (bytebuf-to-string) + (println))) + """, + """ + (do + (load-module :multipart ['multipart :as 'm]) + + (m/render { "Part-1" "xxxxxxxxxxx" + "Part-2" "file:/user/foo/image.png" + "Part-3" (io/file "/user/foo/image.png") + "Part-4" {:name "data.xml" + :mimetype "application/xml" + :data (bytebuf-from-string + "foo" + :utf-8)}}) + """ ) } + + render [data] + (assert (map? data)) + (assert (not (empty? data))) (try-with [os (io/bytebuf-out-stream)] - (doseq [[name value] data] - (if (file-protocol? v) - (io/spit-stream os (to-bytebuf-file name value)) - (io/spit-stream os (to-bytebuf-simple name value))) - @os))) - + (doseq [[name value] data] (render-part name value os)) + @os)) -(defn- to-bytebuf-file [name value] + +(defn- render-part [name value os] + (try + (assert (string? name)) + + (cond + (file-url? value) (->as (. :URL :new value) f + (. f :getPath) + (. :File :new f) + (render-file-part name f os)) + + (io/file? value) (render-file-part name value os) + + (map? value) (render-file-data-part name + (:name part) + (:mimetype part) + (:data part) + os) + (string? value) (render-string-part name value os) + + :else (render-string-part name (str value) os)) + (catch :Exception e + (throw (ex :VncException (str "Failed to process multipart item " name) + e))))) + + +(defn- render-file-part [name file os] (assert (string? name)) + (assert (io/file? file)) - (try-with [os (io/bytebuf-out-stream)] - (let [v-file (->as (. :URL :new value) f - (. f :getPath) - (. :File :new f)) - v-file-name (io/file-path v-file) - v-mime-type (mimetypes/mimetypes/probe-content-type v-file)] - (when (nil? v-mime-type) + (try + (let [v-file-name (io/file-path file) + v-file-mimetype (mimetypes/probe-content-type file) + v-file-data (. :Files :readAllBytes (. file :toPath))] + (when (nil? v-file-mimetype) (throw (ex :VncException (str "Failed to get mimetype for file " v-file-name)))) - (io/spit-stream os (str-to-bytebuf (str dash boundary-value nl))) - (io/spit-stream os (str-to-bytebuf disposition)) - - (io/spit-stream os (str-to-bytebuf (str field-name dq name dq file-name dq v-file-name dq nl))) - (io/spit-stream os (str-to-bytebuf (str content-type v-mime-type nl))) - (io/spit-stream os (str-to-bytebuf nl)) - (io/spit-stream os (. :Files :readAllBytes (. v-file :toPath))) - (io/spit-stream os (str-to-bytebuf nl)) - - (io/spit-stream os (str-to-bytebuf (str dash boundary-value dash nl))) - @os) + (render-file-data-part name v-file-name v-file-mimetype v-file-data os)) (catch :Exception e (throw (ex :VncException (str "Failed do process file " v-file-name) e))))) -(defn- to-bytebuf-simple [name value] - (assert (string? name)) - - (try-with [os (io/bytebuf-out-stream)] - (io/spit-stream os (str-to-bytebuf (str dash boundary-value nl))) - (io/spit-stream os (str-to-bytebuf disposition)) +(defn- render-file-data-part [name v-file-name v-file-mimetype v-file-data os] + (assert (string? v-file-name)) + (assert (string? v-file-mimetype)) + (assert (bytebuf? v-file-data)) - (io/spit-stream os (str-to-bytebuf (str field-name dq name dq nl))) - (io/spit-stream os (str-to-bytebuf nl)) - ((io/spit-stream os str-to-bytebuf (str value nl)) ) + (io/spit-stream os (bytebuf-from-string (str dash boundary-value nl))) + (io/spit-stream os (bytebuf-from-string disposition)) - ((io/spit-stream os str-to-bytebuf (str dash boundary-value dash nl))) - - @os)) + (io/spit-stream os (bytebuf-from-string (str field-name dq name dq file-name dq v-file-name dq nl))) + (io/spit-stream os (bytebuf-from-string (str content-type v-file-mimetype nl))) + (io/spit-stream os (bytebuf-from-string nl)) + (io/spit-stream os v-file-data) + (io/spit-stream os (bytebuf-from-string nl)) + (io/spit-stream os (bytebuf-from-string (str dash boundary-value dash nl)))) + + +(defn- render-string-part [name text os] + (assert (string? name)) + + (io/spit-stream os (bytebuf-from-string (str dash boundary-value nl))) + (io/spit-stream os (bytebuf-from-string disposition)) + (io/spit-stream os (bytebuf-from-string (str field-name dq name dq nl))) + (io/spit-stream os (bytebuf-from-string nl)) + (io/spit-stream os (bytebuf-from-string (str text nl))) -(defn- str-to-bytebuf [s] - (bytebuf-from-string s :UTF-8)) + (io/spit-stream os (bytebuf-from-string (str dash boundary-value dash nl)))) -(defn- file-protocol? [v] +(defn- file-url? [v] (and (string? v) (str/starts-with? v "file:")))