Skip to content

Commit efa5b6f

Browse files
authored
feat(storage): add downloadFile method (#965)
* feat(storage): add `downloadFile` method * fix(storage): add new Swift files to Xcode project * style(storage): fix import ordering * fix(storage): release call on error path and remove unused import
1 parent 78c174d commit efa5b6f

File tree

14 files changed

+417
-2
lines changed

14 files changed

+417
-2
lines changed

.changeset/brave-dogs-cheer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@capacitor-firebase/storage': minor
3+
---
4+
5+
feat: add `downloadFile` method

packages/storage/README.md

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ const useEmulator = async () => {
160160
* [`getMetadata(...)`](#getmetadata)
161161
* [`listFiles(...)`](#listfiles)
162162
* [`updateMetadata(...)`](#updatemetadata)
163+
* [`downloadFile(...)`](#downloadfile)
163164
* [`uploadFile(...)`](#uploadfile)
164165
* [`useEmulator(...)`](#useemulator)
165166
* [Interfaces](#interfaces)
@@ -261,6 +262,32 @@ Update the metadata for a file.
261262
--------------------
262263

263264

265+
### downloadFile(...)
266+
267+
```typescript
268+
downloadFile(options: DownloadFileOptions, callback: DownloadFileCallback) => Promise<CallbackId>
269+
```
270+
271+
Download a file.
272+
273+
On **Android** and **iOS**, the file is downloaded to the local file system
274+
using the `uri` option.
275+
276+
On **Web**, the file is downloaded as a `Blob` and returned in the
277+
callback event.
278+
279+
| Param | Type |
280+
| -------------- | --------------------------------------------------------------------- |
281+
| **`options`** | <code><a href="#downloadfileoptions">DownloadFileOptions</a></code> |
282+
| **`callback`** | <code><a href="#downloadfilecallback">DownloadFileCallback</a></code> |
283+
284+
**Returns:** <code>Promise&lt;string&gt;</code>
285+
286+
**Since:** 8.2.0
287+
288+
--------------------
289+
290+
264291
### uploadFile(...)
265292

266293
```typescript
@@ -406,6 +433,25 @@ On Android, the cleartext traffic must be allowed. On the Capacitor configuratio
406433
| **`customMetadata`** | <code>{ [key: string]: string; }</code> | Additional user-defined custom metadata. | 5.3.0 |
407434

408435

436+
#### DownloadFileOptions
437+
438+
| Prop | Type | Description | Since |
439+
| ---------- | ------------------- | -------------------------------------------------------------------- | ----- |
440+
| **`path`** | <code>string</code> | The full path to the file to download, including the file name. | 8.2.0 |
441+
| **`uri`** | <code>string</code> | The uri to download the file to. Only available for Android and iOS. | 8.2.0 |
442+
443+
444+
#### DownloadFileCallbackEvent
445+
446+
| Prop | Type | Description | Since |
447+
| ---------------------- | -------------------- | ----------------------------------------------------------------------------------- | ----- |
448+
| **`progress`** | <code>number</code> | The download progress, as a percentage between 0 and 1. | 8.2.0 |
449+
| **`bytesTransferred`** | <code>number</code> | The number of bytes that have been transferred. Only available for Android and Web. | 8.2.0 |
450+
| **`totalBytes`** | <code>number</code> | The total number of bytes to be transferred. Only available for Android and Web. | 8.2.0 |
451+
| **`completed`** | <code>boolean</code> | Whether the download is completed or not. | 8.2.0 |
452+
| **`blob`** | <code>Blob</code> | The downloaded file as a Blob. Only available for Web. | 8.2.0 |
453+
454+
409455
#### UploadFileOptions
410456

411457
| Prop | Type | Description | Since |
@@ -444,15 +490,20 @@ On Android, the cleartext traffic must be allowed. On the Capacitor configuratio
444490
### Type Aliases
445491

446492

447-
#### UploadFileCallback
493+
#### DownloadFileCallback
448494

449-
<code>(event: <a href="#uploadfilecallbackevent">UploadFileCallbackEvent</a> | null, error: any): void</code>
495+
<code>(event: <a href="#downloadfilecallbackevent">DownloadFileCallbackEvent</a> | null, error: any): void</code>
450496

451497

452498
#### CallbackId
453499

454500
<code>string</code>
455501

502+
503+
#### UploadFileCallback
504+
505+
<code>(event: <a href="#uploadfilecallbackevent">UploadFileCallbackEvent</a> | null, error: any): void</code>
506+
456507
</docgen-api>
457508

458509
## Changelog

packages/storage/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/storage/FirebaseStorage.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
import com.google.firebase.storage.StorageMetadata;
99
import com.google.firebase.storage.StorageReference;
1010
import com.google.firebase.storage.UploadTask;
11+
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.events.DownloadFileCallbackEvent;
1112
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.events.UploadFileCallbackEvent;
1213
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.DeleteFileOptions;
14+
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.DownloadFileOptions;
1315
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.GetDownloadUrlOptions;
1416
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.GetMetadataOptions;
1517
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.ListFilesOptions;
@@ -18,6 +20,7 @@
1820
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.results.GetDownloadUrlResult;
1921
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.results.GetMetadataResult;
2022
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.results.ListFilesResult;
23+
import io.capawesome.capacitorjs.plugins.firebase.storage.enums.DownloadFileState;
2124
import io.capawesome.capacitorjs.plugins.firebase.storage.enums.UploadFileState;
2225
import io.capawesome.capacitorjs.plugins.firebase.storage.interfaces.EmptyResultCallback;
2326
import io.capawesome.capacitorjs.plugins.firebase.storage.interfaces.NonEmptyEventCallback;
@@ -31,6 +34,28 @@ public FirebaseStorage(FirebaseStoragePlugin plugin) {
3134
this.plugin = plugin;
3235
}
3336

37+
public void downloadFile(@NonNull DownloadFileOptions options, @NonNull NonEmptyEventCallback callback) {
38+
String path = options.getPath();
39+
Uri uri = options.getUri();
40+
41+
StorageReference storageReference = getFirebaseStorageInstance().getReference(path);
42+
storageReference
43+
.getFile(uri)
44+
.addOnProgressListener(taskSnapshot -> {
45+
DownloadFileCallbackEvent result = new DownloadFileCallbackEvent(taskSnapshot, DownloadFileState.RUNNING);
46+
callback.success(result);
47+
})
48+
.addOnSuccessListener(taskSnapshot -> {
49+
DownloadFileCallbackEvent result = new DownloadFileCallbackEvent(taskSnapshot, DownloadFileState.SUCCESS);
50+
callback.success(result);
51+
callback.release();
52+
})
53+
.addOnFailureListener(exception -> {
54+
callback.error(exception);
55+
callback.release();
56+
});
57+
}
58+
3459
public void deleteFile(@NonNull DeleteFileOptions options, @NonNull EmptyResultCallback callback) {
3560
String path = options.getPath();
3661

packages/storage/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/storage/FirebaseStoragePlugin.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.getcapacitor.PluginMethod;
88
import com.getcapacitor.annotation.CapacitorPlugin;
99
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.DeleteFileOptions;
10+
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.DownloadFileOptions;
1011
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.GetDownloadUrlOptions;
1112
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.GetMetadataOptions;
1213
import io.capawesome.capacitorjs.plugins.firebase.storage.classes.options.ListFilesOptions;
@@ -32,6 +33,49 @@ public void load() {
3233
implementation = new FirebaseStorage(this);
3334
}
3435

36+
@PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
37+
public void downloadFile(PluginCall call) {
38+
try {
39+
call.setKeepAlive(true);
40+
41+
String path = call.getString("path");
42+
if (path == null) {
43+
call.reject(ERROR_PATH_MISSING);
44+
return;
45+
}
46+
String uri = call.getString("uri");
47+
if (uri == null) {
48+
call.reject(ERROR_URI_MISSING);
49+
return;
50+
}
51+
String callbackId = call.getCallbackId();
52+
53+
DownloadFileOptions options = new DownloadFileOptions(path, uri, callbackId);
54+
NonEmptyEventCallback callback = new NonEmptyEventCallback() {
55+
@Override
56+
public void success(Result result) {
57+
call.resolve(result.toJSObject());
58+
}
59+
60+
@Override
61+
public void error(Exception exception) {
62+
Logger.error(TAG, exception.getMessage(), exception);
63+
call.reject(exception.getMessage(), FirebaseStorageHelper.createErrorCode(exception));
64+
}
65+
66+
@Override
67+
public void release() {
68+
call.release(bridge);
69+
}
70+
};
71+
72+
implementation.downloadFile(options, callback);
73+
} catch (Exception exception) {
74+
Logger.error(TAG, exception.getMessage(), exception);
75+
call.reject(exception.getMessage(), FirebaseStorageHelper.createErrorCode(exception));
76+
}
77+
}
78+
3579
@PluginMethod
3680
public void deleteFile(PluginCall call) {
3781
try {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.capawesome.capacitorjs.plugins.firebase.storage.classes.events;
2+
3+
import com.getcapacitor.JSObject;
4+
import com.google.firebase.storage.FileDownloadTask;
5+
import io.capawesome.capacitorjs.plugins.firebase.storage.enums.DownloadFileState;
6+
import io.capawesome.capacitorjs.plugins.firebase.storage.interfaces.Result;
7+
8+
public class DownloadFileCallbackEvent implements Result {
9+
10+
private FileDownloadTask.TaskSnapshot taskSnapshot;
11+
private DownloadFileState state;
12+
13+
public DownloadFileCallbackEvent(FileDownloadTask.TaskSnapshot taskSnapshot, DownloadFileState state) {
14+
this.taskSnapshot = taskSnapshot;
15+
this.state = state;
16+
}
17+
18+
public JSObject toJSObject() {
19+
JSObject result = new JSObject();
20+
result.put("progress", (double) taskSnapshot.getBytesTransferred() / taskSnapshot.getTotalByteCount());
21+
result.put("bytesTransferred", taskSnapshot.getBytesTransferred());
22+
result.put("totalBytes", taskSnapshot.getTotalByteCount());
23+
result.put("completed", state == DownloadFileState.SUCCESS);
24+
return result;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.capawesome.capacitorjs.plugins.firebase.storage.classes.options;
2+
3+
import android.net.Uri;
4+
5+
public class DownloadFileOptions {
6+
7+
private String path;
8+
private Uri uri;
9+
private String callbackId;
10+
11+
public DownloadFileOptions(String path, String uri, String callbackId) {
12+
this.path = path;
13+
this.uri = Uri.parse(uri);
14+
this.callbackId = callbackId;
15+
}
16+
17+
public String getPath() {
18+
return path;
19+
}
20+
21+
public Uri getUri() {
22+
return uri;
23+
}
24+
25+
public String getCallbackId() {
26+
return callbackId;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package io.capawesome.capacitorjs.plugins.firebase.storage.enums;
2+
3+
public enum DownloadFileState {
4+
RUNNING,
5+
SUCCESS
6+
}

packages/storage/ios/Plugin.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
7C7A8D7E2ADBCD8400C195CB /* UpdateMetadataOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C7A8D7D2ADBCD8400C195CB /* UpdateMetadataOptions.swift */; };
2727
7C7A8D802ADBCD8C00C195CB /* UploadFileOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C7A8D7F2ADBCD8C00C195CB /* UploadFileOptions.swift */; };
2828
7C7A8D832ADBCD9C00C195CB /* UploadFileCallbackEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C7A8D822ADBCD9C00C195CB /* UploadFileCallbackEvent.swift */; };
29+
7C7A8D852ADCAE0100C195CB /* DownloadFileOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C7A8D842ADCAE0100C195CB /* DownloadFileOptions.swift */; };
30+
7C7A8D872ADCAE0200C195CB /* DownloadFileCallbackEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C7A8D862ADCAE0200C195CB /* DownloadFileCallbackEvent.swift */; };
2931
/* End PBXBuildFile section */
3032

3133
/* Begin PBXContainerItemProxy section */
@@ -61,6 +63,8 @@
6163
7C7A8D7D2ADBCD8400C195CB /* UpdateMetadataOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMetadataOptions.swift; sourceTree = "<group>"; };
6264
7C7A8D7F2ADBCD8C00C195CB /* UploadFileOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadFileOptions.swift; sourceTree = "<group>"; };
6365
7C7A8D822ADBCD9C00C195CB /* UploadFileCallbackEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadFileCallbackEvent.swift; sourceTree = "<group>"; };
66+
7C7A8D842ADCAE0100C195CB /* DownloadFileOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadFileOptions.swift; sourceTree = "<group>"; };
67+
7C7A8D862ADCAE0200C195CB /* DownloadFileCallbackEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadFileCallbackEvent.swift; sourceTree = "<group>"; };
6468
91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = "<group>"; };
6569
96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = "<group>"; };
6670
F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = "<group>"; };
@@ -153,6 +157,7 @@
153157
isa = PBXGroup;
154158
children = (
155159
7C7A8D752ADBCD6200C195CB /* DeleteFileOptions.swift */,
160+
7C7A8D842ADCAE0100C195CB /* DownloadFileOptions.swift */,
156161
7C7A8D772ADBCD6B00C195CB /* GetDownloadUrlOptions.swift */,
157162
7C7A8D792ADBCD7400C195CB /* GetMetadataOptions.swift */,
158163
7C7A8D7B2ADBCD7C00C195CB /* ListFilesOptions.swift */,
@@ -175,6 +180,7 @@
175180
7C7A8D812ADBCD9500C195CB /* Events */ = {
176181
isa = PBXGroup;
177182
children = (
183+
7C7A8D862ADCAE0200C195CB /* DownloadFileCallbackEvent.swift */,
178184
7C7A8D822ADBCD9C00C195CB /* UploadFileCallbackEvent.swift */,
179185
);
180186
path = Events;
@@ -396,6 +402,8 @@
396402
50E1A94820377CB70090CE1A /* FirebaseStoragePlugin.swift in Sources */,
397403
7C7A8D7E2ADBCD8400C195CB /* UpdateMetadataOptions.swift in Sources */,
398404
7C7A8D762ADBCD6200C195CB /* DeleteFileOptions.swift in Sources */,
405+
7C7A8D852ADCAE0100C195CB /* DownloadFileOptions.swift in Sources */,
406+
7C7A8D872ADCAE0200C195CB /* DownloadFileCallbackEvent.swift in Sources */,
399407
7C7A8D832ADBCD9C00C195CB /* UploadFileCallbackEvent.swift in Sources */,
400408
2F98D68224C9AAE500613A4C /* FirebaseStorage.swift in Sources */,
401409
7C7A8D7A2ADBCD7400C195CB /* GetMetadataOptions.swift in Sources */,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Foundation
2+
import Capacitor
3+
import FirebaseStorage
4+
5+
@objc public class DownloadFileCallbackEvent: NSObject, Result {
6+
private var snapshot: StorageTaskSnapshot
7+
8+
init(snapshot: StorageTaskSnapshot) {
9+
self.snapshot = snapshot
10+
}
11+
12+
public func toJSObject() -> AnyObject {
13+
var result = JSObject()
14+
result["progress"] = Double(snapshot.progress!.completedUnitCount)
15+
/ Double(snapshot.progress!.totalUnitCount)
16+
result["completed"] = snapshot.status == .success ? true : false
17+
return result as AnyObject
18+
}
19+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Foundation
2+
3+
@objc public class DownloadFileOptions: NSObject {
4+
private var path: String
5+
private var uri: URL
6+
private var callbackId: String
7+
8+
init(path: String, uri: URL, callbackId: String) {
9+
self.path = path
10+
self.uri = uri
11+
self.callbackId = callbackId
12+
}
13+
14+
func getPath() -> String {
15+
return path
16+
}
17+
18+
func getUri() -> URL {
19+
return uri
20+
}
21+
22+
func getCallbackId() -> String {
23+
return callbackId
24+
}
25+
}

0 commit comments

Comments
 (0)