-
-
Notifications
You must be signed in to change notification settings - Fork 192
RFC: Add support for resumable uploads #794
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Hey @cwalo thanks for this proposal, I'll check it soon and post my thoughts on this here. Thanks again. |
Hey @cwalo, thanks a lot for your excellent work on this proposal. Here are a few things to consider: If we decide to pursue this path, which I’m still uncertain about, I’d prefer not to rely directly on the Another option is that iOS 17 already supports the TUS protocol by default. We could implement TUS using the native Apple implementation and make it available only for iOS 17+. This is the approach I’m more inclined to take. Let me know your thoughts regarding these points. |
Thanks for taking a look @grdsdev!
I don't think that would be too hard - just need to think through where we'd hook in. Are there any examples of this approach for other "add-ons?"
Based on my research, the IETF standard is similar to, but distinct from, the TUS v1 protocol. Unless the supabase backend supported both, I don't believe the Foundation implementation would work. |
I didn’t know that. From a quick glance, I thought they were the same. I’ll check internally with the storage team to see if Apple’s implementation can be easily supported.
No examples are provided, so this would be the first “add-on.” We could integrate it into the configuration for Supabase client. Additionally, we could introduce a new option for a Another option worth considering is to implement something similar to the Kotlin library. It implements the resumable protocol without relying on any external dependencies. You can check it out at https://github.com/supabase-community/supabase-kt/tree/master/Storage/src/commonMain/kotlin/io/github/jan/supabase/storage/resumable. We could use this implementation to offer a first-party resumable upload implementation as well. |
I was just poking at something like that. Let me play with creating a separate package and hooking into the StorageClientConfiguration.
I looked at that first for inspo and actually tried to mirror the user-facing api - |
I think this might do the trick Define a protocol with associated types: public protocol ResumableUploadProtocol: Sendable {
associatedtype UploadType
associatedtype UploadStatus
init(bucketId: String, url: URL, headers: [String: String])
func upload(file: URL, to path: String, options: FileOptions) async throws -> UploadType
func upload(data: Data, to path: String, pathExtension: String?, options: FileOptions) async throws -> UploadType
func pauseUpload(id: UUID) async throws
func pauseAllUploads() async throws
func resumeUpload(id: UUID) async throws -> Bool
func retryUpload(id: UUID) async throws -> Bool
func resumeAllUploads() async throws
func cancelUpload(id: UUID) async throws
func cancelAllUploads() async throws
func getUploadStatus(id: UUID) async throws -> UploadStatus?
func getUpload(id: UUID) async throws -> UploadType?
} Provide the configuration with a closure that returns an instance: public struct StorageClientConfiguration Sendable {
public var url: URL
public var headers: [String: String]
public let resumable: (@Sendable (_ bucketId: String, _ url: URL, _ headers: [String: String]) -> any ResumableUploadProtocol)?,
} To avoid littering the storage apis with generics, we can extend the StorageFileApi with a function getter that casts the return type: extension StorageFileApi {
// Calls the provided getter, casting to the requested concrete type
public func resumable<R: ResumableUploadProtocol>() -> R? {
configuration.resumable?(bucketId, configuration.url.appendingPathComponent("upload/resumable/"), configuration.headers) as? R
}
}
let api: MyResumableUploadAPI? = supabase.storage.from("bucket").resumable() Then consumers can create and store clients however they please. |
With the addition of the protocol, I'll probably still want to package my implementation separately just because it provides a nice concurrency api over TUSKit :) |
@grdsdev Nice! I'll check it out. |
We initiated an internal discussion about providing first-party support for TUS in the libraries. Once we have a minimal design, I’ll share it with the community to gather feedback. I’ll keep this PR open for reference until then. |
What kind of change does this PR introduce?
This PR introduces a
ResumableUploadApi
, integrating TUSKit, for resumable upload support. So far this adds basic functionality for uploading individual files and data blobs, canceling/resuming uploads, observing upload status, and configuring the client with a background session configuration.At the moment I'm looking for feedback on the api and any considerations I might be missing.
Design Details:
SupabaseStorageClient
is initialized with aResumableClientStore
, creating an instance ofResumableUploadApi
ResumableUploadApi
is the public interface for interacting with the client and uploading files.ResumableClientStore
is an actor responsible for lazily creating and storing instances ofResumableUploadClient
, keyed by bucketId - one instance per bucket.ResumableUploadClient
wraps TUSClient and tracks active/finished uploadsResumableUpload
is a reference type that can be used to observe the status of an upload, pause, and resumeupload.status()
, creating anAsyncStream<ResumableUpload.Status>
. The stream will replay the last emitted status and a stream can be created an any time, as long as you have a handle to the upload. Multiple streams can be created.New dependencies:
Considerations:
TUSClient.reset
without affecting other buckets.ResumableUploadClient
initializer non-throwing by initializing the TUSClient lazily, but I don't have a strong opinion yet.TODOs:
Basic upload:
Resume stored (not failed) uploads on launch:
Pause and resume an upload:
More examples in
Tests/StorageTests/ResumableTests
What is the current behavior?
#171