dependencies
| (this space intentionally left almost blank) | |||||||||
Functions to interact with Amazon S3 storage. All functions take AmazonS3Client instance as the first parameter. Exception: | (ns clj-s3-client.core
(:require [camel-snake-kebab.core :refer [->kebab-case-keyword]]
[camel-snake-kebab.extras :refer [transform-keys]]
[clojure.set :refer [difference]])
(:import [com.amazonaws.services.s3 AmazonS3Client AmazonS3URI]
[com.amazonaws.services.s3.model CannedAccessControlList PutObjectRequest AmazonS3Exception ObjectMetadata]
[com.amazonaws ClientConfiguration]
[com.amazonaws.regions Regions]
[java.io InputStream]
[java.nio ByteBuffer])) | |||||||||
(defn- acl->access-control-list [acl]
(get {:private CannedAccessControlList/Private
:public-read CannedAccessControlList/PublicRead
:public-read-write CannedAccessControlList/PublicReadWrite
:aws-exec-read CannedAccessControlList/AwsExecRead
:authenticated-read CannedAccessControlList/AuthenticatedRead
:bucket-owner-read CannedAccessControlList/BucketOwnerRead
:bucket-owner-full-control CannedAccessControlList/BucketOwnerFullControl
:log-delivery-write CannedAccessControlList/LogDeliveryWrite} acl CannedAccessControlList/Private)) | ||||||||||
(defn- object-metadata->map [^ObjectMetadata s3-object-metadata]
(let [s3-object-raw-metadata (into {} (.getRawMetadata s3-object-metadata))
s3-user-metadata (into {} (.getUserMetadata s3-object-metadata))]
(transform-keys ->kebab-case-keyword (conj s3-object-raw-metadata s3-user-metadata)))) | ||||||||||
(defn- map->object-metadata [metadata]
(let [object-metadata (ObjectMetadata.)
supported {:content-length (fn [ob value] (doto ob (.setContentLength value)))
:content-type (fn [ob value] (doto ob (.setContentType value)))
:content-language (fn [ob value] (doto ob (.setContentLanguage value)))
:content-encoding (fn [ob value] (doto ob (.setContentEncoding value)))
:content-disposition (fn [ob value] (doto ob (.setContentDisposition value)))
:http-expires-date (fn [ob value] (doto ob (.setHttpExpiresDate value)))}
specific-setters (select-keys supported (keys metadata))
user-meta-fields (difference (set (keys metadata)) (set (keys specific-setters)))
user-metadata-setter (reduce (fn [acc k] (assoc acc k (fn [ob value] (doto ob (.addUserMetadata (name k) value))))) {} user-meta-fields)]
(reduce-kv
(fn [ob k v] (v ob (get metadata k)))
object-metadata
(conj specific-setters user-metadata-setter)))) | ||||||||||
(defn- suppress-not-found-exception [exception]
(when-not (= 404 (.getStatusCode exception))
(throw exception))) | ||||||||||
(create-client)Returns an instance of AmazonS3Client which can be used with all other functions to access S3 resources. An optional map of options can include any of the following keys:
| (defn create-client
[& {:keys [max-connections max-error-retry endpoint region tcp-keep-alive]
:or {max-connections 50 max-error-retry 1 tcp-keep-alive false}}]
(let [configuration (-> (ClientConfiguration.)
(.withMaxErrorRetry max-error-retry)
(.withMaxConnections max-connections)
(.withTcpKeepAlive tcp-keep-alive))
client (AmazonS3Client. configuration)]
(when endpoint
(.setEndpoint client endpoint))
(if region
(.withRegion client (Regions/fromName region))
client))) | |||||||||
(create-bucket client "my-awesome-bucket") Creates a bucket | (defn create-bucket [^AmazonS3Client client bucket-name] (.createBucket client bucket-name)) | |||||||||
(delete-bucket client "my-not-so-awesome-bucket") Deletes the bucket | (defn delete-bucket [^AmazonS3Client client bucket-name] (.deleteBucket client bucket-name)) | |||||||||
(bucket-exists? client "my-awesome-bucket") Checks if bucket Returns | (defn bucket-exists? [^AmazonS3Client client bucket-name] (.doesBucketExist client bucket-name)) | |||||||||
(put-object client "my-awesome-bucket" "my-awesome-me.txt" file-input-stream {:acl :private :content-length 1234 :content-type "text/html"})Store the input stream to s3 with given options. An optional map of options can include any of the following keys:
The map can also contain other keys which are added as custom headers for the file. | (defn put-object
[^AmazonS3Client client bucket-name object-key ^InputStream is options]
(let [metadata (dissoc options :acl)
acl (:acl options)
s3-object-metadata (map->object-metadata metadata)
put-object-req (.withCannedAcl
(PutObjectRequest. bucket-name object-key is s3-object-metadata)
(acl->access-control-list acl))
put-object-resp (.putObject client put-object-req)
result-s3-object-metadata (object-metadata->map (.getMetadata put-object-resp))]
(assoc result-s3-object-metadata :object-key object-key :bucket-name bucket-name :content is))) | |||||||||
(get-object client "my-awesome-bucket" "my-awesome-me.txt") Fetches the object from bucket Returns | (defn get-object
[^AmazonS3Client client bucket-name object-key]
(try
(let [s3-object (.getObject client bucket-name object-key)
s3-object-metadata (object-metadata->map (.getObjectMetadata s3-object))]
(assoc s3-object-metadata :object-key (.getKey s3-object) :bucket-name (.getBucketName s3-object) :content (.getObjectContent s3-object)))
(catch AmazonS3Exception e (suppress-not-found-exception e)))) | |||||||||
(delete-object client "my-awesome-bucket" "my-awesome-me.txt") Destroys the object | (defn delete-object [^AmazonS3Client client bucket-name object-key] (.deleteObject client bucket-name object-key)) | |||||||||
(bucket-name-and-object-key->url client "my-awesome-bucket" "my-awesome-me.txt") Returns the corresponding url of However, depending on the permissions it might or might not work. | (defn bucket-name-and-object-key->url [^AmazonS3Client client bucket-name object-key] (str (.getUrl client bucket-name object-key))) | |||||||||
(url->bucketname-and-object-key "my-awesome-bucket" "my-awesome-me.txt") Returns the bucket-name and object-key of | (defn url->bucket-name-and-object-key
[url]
(let [s3-uri (AmazonS3URI. url true)
bucket-name (.getBucket s3-uri)
object-key (.getKey s3-uri)]
{:bucket-name bucket-name
:object-key object-key})) | |||||||||