storage
File storage abstraction with local filesystem and AWS S3 backends. Includes file validation, image processing, and signed URL generation.
Key namespaces
| Namespace | Purpose |
|---|---|
|
Pure: file validation (size, type, extension), filename sanitization |
|
Protocols: |
|
Malli schemas: |
|
Service layer orchestrating validation + storage |
|
Local filesystem adapter (content-addressed with SHA-256) |
|
AWS S3 / S3-compatible adapter (MinIO, DigitalOcean Spaces) |
|
Java AWT image processing (no external deps) |
|
Ring handlers for upload/download/delete |
Storage operations
(require '[boundary.storage.ports :as ports])
(ports/store-file storage
{:bytes (.getBytes "content") :content-type "text/plain"}
{:filename "test.txt"})
;=> {:key "ab/1234-uuid-hash.txt" :url "..." :size 7 :stored-at #inst"..."}
(ports/retrieve-file storage "ab/1234-uuid-hash.txt")
(ports/file-exists? storage "ab/1234-uuid-hash.txt")
(ports/delete-file storage "ab/1234-uuid-hash.txt")
(ports/generate-signed-url storage key 3600) ; S3 only
File validation
(require '[boundary.storage.core.validation :as validation])
(validation/validate-file
{:bytes file-bytes :content-type "image/png" :filename "photo.png"}
{:max-size-bytes (* 5 1024 1024)
:allowed-types #{"image/jpeg" "image/png" "image/webp"}
:allowed-exts #{".jpg" ".jpeg" ".png" ".webp"}})
Image processing
(ports/resize-image processor file-bytes {:width 800 :height 600 :maintain-aspect? true})
(ports/create-thumbnail processor file-bytes {:width 150 :height 150})
Storage key format
-
Local:
{shard}/{timestamp}-{uuid}-{hash16}.{ext}(shard = first 2 chars of SHA-256) -
S3:
{prefix}/{timestamp}-{uuid}-{hash16}.{ext}
Configuration
;; Local (development)
:boundary/storage {:backend :local :base-path "/var/uploads" :base-url "http://localhost:3000/uploads"}
;; S3 (production)
:boundary/storage {:backend :s3 :bucket #env AWS_S3_BUCKET :region #env AWS_REGION
:access-key #env AWS_ACCESS_KEY_ID :secret-key #env AWS_SECRET_ACCESS_KEY}
Testing
clojure -M:test:db/h2 :storage