Skip to content

Commit

Permalink
Refactor Storage download & upload (flow) implementation (#729)
Browse files Browse the repository at this point in the history
* Add support for file metadata, `info` and `exists`

* Finish up `info` method and rename `BucketItem` to `FileObject`

* Add some missing docs and tests

* fix docs

* suppress warning

* suppress warning for signed urls

* Move upsert parameter to new FileOptionBuilder

* remove comma

* remove println

* Refactor Storage download & upload (flow) implementation

* Revert accidental change

* Remove file object V2

* Fix type mismatch
  • Loading branch information
jan-tennert authored Sep 17, 2024
1 parent 0961cb0 commit 6689099
Show file tree
Hide file tree
Showing 13 changed files with 489 additions and 382 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,170 +11,178 @@ import kotlin.io.path.writeBytes
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return the key to the uploaded file
*/
suspend fun BucketApi.upload(path: String, file: File, upsert: Boolean = false) = upload(path, UploadData(file.readChannel(), file.length()), upsert)
suspend fun BucketApi.upload(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = upload(path, UploadData(file.readChannel(), file.length()), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
fun BucketApi.uploadAsFlow(path: String, file: File, upsert: Boolean = false) = uploadAsFlow(path, UploadData(file.readChannel(), file.length()), upsert)
fun BucketApi.uploadAsFlow(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = uploadAsFlow(path, UploadData(file.readChannel(), file.length()), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return the key to the uploaded file
*/
suspend fun BucketApi.upload(path: String, file: Path, upsert: Boolean = false) = upload(path, UploadData(file.readChannel(), file.fileSize()), upsert)
suspend fun BucketApi.upload(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = upload(path, UploadData(file.readChannel(), file.fileSize()), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
fun BucketApi.uploadAsFlow(path: String, file: Path, upsert: Boolean = false) = uploadAsFlow(path, UploadData(file.readChannel(), file.fileSize()), upsert)
fun BucketApi.uploadAsFlow(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = uploadAsFlow(path, UploadData(file.readChannel(), file.fileSize()), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
* @param token The presigned url token
* @param token The pre-signed url token
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return the key to the uploaded file
*/
suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: File, upsert: Boolean = false) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.length()), upsert)
suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.length()), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
* @param token The presigned url token
* @param token The pre-signed url token
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: File, upsert: Boolean = false) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.length()), upsert)
fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.length()), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
* @param token The presigned url token
* @param token The pre-signed url token
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
*/
suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: Path, upsert: Boolean = false) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.fileSize()), upsert)
suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrl(path, token, UploadData(file.readChannel(), file.fileSize()), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
* @param token The presigned url token
* @param token The pre-signed url token
* @param file The file to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: Path, upsert: Boolean = false) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.fileSize()), upsert)
fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrlAsFlow(path, token, UploadData(file.readChannel(), file.fileSize()), options)

/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
*/
suspend fun BucketApi.update(path: String, file: Path, upsert: Boolean = false) = update(path, UploadData(file.readChannel(), file.fileSize()), upsert)
suspend fun BucketApi.update(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = update(path, UploadData(file.readChannel(), file.fileSize()), options)

/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
fun BucketApi.updateAsFlow(path: String, file: Path, upsert: Boolean = false) = updateAsFlow(path, UploadData(file.readChannel(), file.fileSize()), upsert)
fun BucketApi.updateAsFlow(path: String, file: Path, options: UploadOptionBuilder.() -> Unit = {}) = updateAsFlow(path, UploadData(file.readChannel(), file.fileSize()), options)

/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
*/
suspend fun BucketApi.update(path: String, file: File, upsert: Boolean = false) = update(path, UploadData(file.readChannel(), file.length()), upsert)
suspend fun BucketApi.update(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = update(path, UploadData(file.readChannel(), file.length()), options)

/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to be updated
* @param file The new file
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the uploaded file
*/
fun BucketApi.updateAsFlow(path: String, file: File, upsert: Boolean = false) = updateAsFlow(path, UploadData(file.readChannel(), file.length()), upsert)
fun BucketApi.updateAsFlow(path: String, file: File, options: UploadOptionBuilder.() -> Unit = {}) = updateAsFlow(path, UploadData(file.readChannel(), file.length()), options)

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
*/
suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticated(path, file.writeChannel(), transform)
suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticated(path, file.writeChannel(), options)

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.writeChannel(), transform)
fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.writeChannel(), options)

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
*/
suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticated(path, file.toFile().writeChannel(), transform)
suspend fun BucketApi.downloadAuthenticatedTo(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticated(path, file.toFile().writeChannel(), options)

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.toFile().writeChannel(), transform)
fun BucketApi.downloadAuthenticatedToAsFlow(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) = downloadAuthenticatedAsFlow(path, file.toFile().writeChannel(), options)

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
*/
suspend fun BucketApi.downloadPublicTo(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadPublic(path, file.writeChannel(), transform)
suspend fun BucketApi.downloadPublicTo(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadPublic(path, file.writeChannel(), options)

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
fun BucketApi.downloadPublicToAsFlow(path: String, file: File, transform: ImageTransformation.() -> Unit = {}) = downloadPublicAsFlow(path, file.writeChannel(), transform)
fun BucketApi.downloadPublicToAsFlow(path: String, file: File, options: DownloadOptionBuilder.() -> Unit = {}) = downloadPublicAsFlow(path, file.writeChannel(), options)

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
*/
suspend fun BucketApi.downloadPublicTo(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) {
val bytes = downloadPublic(path, transform)
suspend fun BucketApi.downloadPublicTo(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) {
val bytes = downloadPublic(path, options)
file.writeBytes(bytes)
}

/**
* Downloads a file from [BucketApi.bucketId] under [path] and saves it to [file]
* @param path The path to download the file from
* @param file The file to save the data to
* @param options Additional options for the download
* @return A flow that emits the download progress and at last the key to the downloaded file
*/
fun BucketApi.downloadPublicToAsFlow(path: String, file: Path, transform: ImageTransformation.() -> Unit = {}) = downloadPublicAsFlow(path, file.toFile().writeChannel(), transform)
fun BucketApi.downloadPublicToAsFlow(path: String, file: Path, options: DownloadOptionBuilder.() -> Unit = {}) = downloadPublicAsFlow(path, file.toFile().writeChannel(), options)
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,56 @@ import io.ktor.utils.io.jvm.javaio.toByteReadChannel
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param uri The uri to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return the key to the updated file
*/
suspend fun BucketApi.upload(path: String, uri: Uri, upsert: Boolean = false) = upload(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
suspend fun BucketApi.upload(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = upload(path, UploadData(uri.readChannel(), uri.contentSize), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path]
* @param path The path to upload the file to
* @param uri The uri to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the updated file
*/
fun BucketApi.uploadAsFlow(path: String, uri: Uri, upsert: Boolean = false) = uploadAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
fun BucketApi.uploadAsFlow(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = uploadAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
* @param token The presigned url token
* @param token The pre-signed url token
* @param uri The uri to upload
* @return the key to the updated file
*/
suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, uri: Uri, upsert: Boolean = false) = uploadToSignedUrl(path, token, UploadData(uri.readChannel(), uri.contentSize), upsert)
suspend fun BucketApi.uploadToSignedUrl(path: String, token: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrl(path, token, UploadData(uri.readChannel(), uri.contentSize), options)

/**
* Uploads a file in [BucketApi.bucketId] under [path] using a presigned url
* @param path The path to upload the file to
* @param token The presigned url token
* @param token The pre-signed url token
* @param uri The uri to upload
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the updated file
*/
fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, uri: Uri, upsert: Boolean = false) = uploadToSignedUrlAsFlow(path, token, UploadData(uri.readChannel(), uri.contentSize), upsert)
fun BucketApi.uploadToSignedUrlAsFlow(path: String, token: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = uploadToSignedUrlAsFlow(path, token, UploadData(uri.readChannel(), uri.contentSize), options)

/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to update the file to
* @param uri The uri to update
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return the key to the updated file
*/
suspend fun BucketApi.update(path: String, uri: Uri, upsert: Boolean = false) = update(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
suspend fun BucketApi.update(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = update(path, UploadData(uri.readChannel(), uri.contentSize), options)

/**
* Updates a file in [BucketApi.bucketId] under [path]
* @param path The path to update the file to
* @param uri The uri to update
* @param upsert Whether to overwrite an existing file
* @param options Additional options for the upload
* @return A flow that emits the upload progress and at last the key to the updated file
*/
fun BucketApi.updateAsFlow(path: String, uri: Uri, upsert: Boolean = false) = updateAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), upsert)
fun BucketApi.updateAsFlow(path: String, uri: Uri, options: UploadOptionBuilder.() -> Unit = {}) = updateAsFlow(path, UploadData(uri.readChannel(), uri.contentSize), options)

@SuppressLint("Recycle") //toByteReadChannel closes the input stream automatically
private fun Uri.readChannel(): ByteReadChannel {
Expand Down
Loading

0 comments on commit 6689099

Please sign in to comment.