44import android .os .Environment ;
55
66import java .io .File ;
7+ import java .io .FileOutputStream ;
8+ import java .io .IOException ;
9+ import java .io .InputStream ;
710import java .util .Map ;
811import java .util .HashMap ;
912
1720import com .facebook .react .bridge .WritableMap ;
1821import com .facebook .react .bridge .ReactMethod ;
1922import com .facebook .react .bridge .ReadableMap ;
23+ import com .facebook .react .bridge .ReactContext ;
2024import com .facebook .react .bridge .ReactApplicationContext ;
2125import com .facebook .react .bridge .ReactContextBaseJavaModule ;
2226
2327import com .google .android .gms .tasks .Task ;
2428import com .google .android .gms .tasks .OnFailureListener ;
2529import com .google .android .gms .tasks .OnSuccessListener ;
2630
31+ import com .google .firebase .storage .StorageException ;
32+ import com .google .firebase .storage .StreamDownloadTask ;
2733import com .google .firebase .storage .UploadTask ;
2834import com .google .firebase .storage .FirebaseStorage ;
2935import com .google .firebase .storage .StorageMetadata ;
@@ -49,6 +55,18 @@ public class FirestackStorage extends ReactContextBaseJavaModule {
4955 private static final String FileTypeRegular = "FILETYPE_REGULAR" ;
5056 private static final String FileTypeDirectory = "FILETYPE_DIRECTORY" ;
5157
58+ private static final String STORAGE_UPLOAD_PROGRESS = "upload_progress" ;
59+ private static final String STORAGE_UPLOAD_PAUSED = "upload_paused" ;
60+ private static final String STORAGE_UPLOAD_RESUMED = "upload_resumed" ;
61+
62+ private static final String STORAGE_DOWNLOAD_PROGRESS = "download_progress" ;
63+ private static final String STORAGE_DOWNLOAD_PAUSED = "download_paused" ;
64+ private static final String STORAGE_DOWNLOAD_RESUMED = "download_resumed" ;
65+ private static final String STORAGE_DOWNLOAD_SUCCESS = "download_success" ;
66+ private static final String STORAGE_DOWNLOAD_FAILURE = "download_failure" ;
67+
68+ private ReactContext mReactContext ;
69+
5270 public FirestackStorage (ReactApplicationContext reactContext ) {
5371 super (reactContext );
5472
@@ -60,14 +78,123 @@ public String getName() {
6078 return TAG ;
6179 }
6280
81+
82+ public boolean isExternalStorageWritable () {
83+ String state = Environment .getExternalStorageState ();
84+ return Environment .MEDIA_MOUNTED .equals (state );
85+ }
86+
87+ @ ReactMethod
88+ public void downloadFile (final String urlStr ,
89+ final String fbPath ,
90+ final String localFile ,
91+ final Callback callback ) {
92+ Log .d (TAG , "downloadFile: " + urlStr + ", " + localFile );
93+ if (!isExternalStorageWritable ()) {
94+ Log .w (TAG , "downloadFile failed: external storage not writable" );
95+ WritableMap error = Arguments .createMap ();
96+ final int errorCode = 1 ;
97+ error .putDouble ("code" , errorCode );
98+ error .putString ("description" , "downloadFile failed: external storage not writable" );
99+ callback .invoke (error );
100+ return ;
101+ }
102+ FirebaseStorage storage = FirebaseStorage .getInstance ();
103+ String storageBucket = storage .getApp ().getOptions ().getStorageBucket ();
104+ String storageUrl = "gs://" + storageBucket ;
105+ Log .d (TAG , "Storage url " + storageUrl + fbPath );
106+
107+ StorageReference storageRef = storage .getReferenceFromUrl (storageUrl );
108+ StorageReference fileRef = storageRef .child (fbPath );
109+
110+ fileRef .getStream (new StreamDownloadTask .StreamProcessor () {
111+ @ Override
112+ public void doInBackground (StreamDownloadTask .TaskSnapshot taskSnapshot , InputStream inputStream ) throws IOException {
113+ int indexOfLastSlash = localFile .lastIndexOf ("/" );
114+ String pathMinusFileName = localFile .substring (0 , indexOfLastSlash ) + "/" ;
115+ String filename = localFile .substring (indexOfLastSlash + 1 );
116+ File fileWithJustPath = new File (pathMinusFileName );
117+ if (!fileWithJustPath .mkdirs ()) {
118+ Log .e (TAG , "Directory not created" );
119+ WritableMap error = Arguments .createMap ();
120+ error .putString ("message" , "Directory not created" );
121+ callback .invoke (error );
122+ return ;
123+ }
124+ File fileWithFullPath = new File (pathMinusFileName , filename );
125+ FileOutputStream output = new FileOutputStream (fileWithFullPath );
126+ int bufferSize = 1024 ;
127+ byte [] buffer = new byte [bufferSize ];
128+ int len = 0 ;
129+ while ((len = inputStream .read (buffer )) != -1 ) {
130+ output .write (buffer , 0 , len );
131+ }
132+ output .close ();
133+ }
134+ }).addOnProgressListener (new OnProgressListener <StreamDownloadTask .TaskSnapshot >() {
135+ @ Override
136+ public void onProgress (StreamDownloadTask .TaskSnapshot taskSnapshot ) {
137+ WritableMap data = Arguments .createMap ();
138+ data .putString ("ref" , taskSnapshot .getStorage ().getBucket ());
139+ double percentComplete = taskSnapshot .getTotalByteCount () == 0 ? 0.0f : 100.0f * (taskSnapshot .getBytesTransferred ()) / (taskSnapshot .getTotalByteCount ());
140+ data .putDouble ("progress" , percentComplete );
141+ Utils .sendEvent (mReactContext , STORAGE_DOWNLOAD_PROGRESS , data );
142+ }
143+ }).addOnPausedListener (new OnPausedListener <StreamDownloadTask .TaskSnapshot >() {
144+ @ Override
145+ public void onPaused (StreamDownloadTask .TaskSnapshot taskSnapshot ) {
146+ WritableMap data = Arguments .createMap ();
147+ data .putString ("ref" , taskSnapshot .getStorage ().getBucket ());
148+ Utils .sendEvent (mReactContext , STORAGE_DOWNLOAD_PAUSED , data );
149+ }
150+ }).addOnSuccessListener (new OnSuccessListener <StreamDownloadTask .TaskSnapshot >() {
151+ @ Override
152+ public void onSuccess (StreamDownloadTask .TaskSnapshot taskSnapshot ) {
153+ final WritableMap data = Arguments .createMap ();
154+ StorageReference ref = taskSnapshot .getStorage ();
155+ data .putString ("fullPath" , ref .getPath ());
156+ data .putString ("bucket" , ref .getBucket ());
157+ data .putString ("name" , ref .getName ());
158+ ref .getMetadata ().addOnSuccessListener (new OnSuccessListener <StorageMetadata >() {
159+ @ Override
160+ public void onSuccess (final StorageMetadata storageMetadata ) {
161+ data .putMap ("metadata" , getMetadataAsMap (storageMetadata ));
162+ callback .invoke (null , data );
163+ }
164+ })
165+ .addOnFailureListener (new OnFailureListener () {
166+ @ Override
167+ public void onFailure (@ NonNull Exception exception ) {
168+ final int errorCode = 1 ;
169+ WritableMap data = Arguments .createMap ();
170+ StorageException storageException = StorageException .fromException (exception );
171+ data .putString ("description" , storageException .getMessage ());
172+ data .putInt ("code" , errorCode );
173+ callback .invoke (makeErrorPayload (errorCode , exception ));
174+ }
175+ });
176+ }
177+ }).addOnFailureListener (new OnFailureListener () {
178+ @ Override
179+ public void onFailure (@ NonNull Exception exception ) {
180+ final int errorCode = 1 ;
181+ WritableMap data = Arguments .createMap ();
182+ StorageException storageException = StorageException .fromException (exception );
183+ data .putString ("description" , storageException .getMessage ());
184+ data .putInt ("code" , errorCode );
185+ callback .invoke (makeErrorPayload (errorCode , exception ));
186+ }
187+ });
188+ }
189+
63190 @ ReactMethod
64191 public void downloadUrl (final String javascriptStorageBucket ,
65192 final String path ,
66193 final Callback callback ) {
67194 FirebaseStorage storage = FirebaseStorage .getInstance ();
68195 String storageBucket = storage .getApp ().getOptions ().getStorageBucket ();
69196 String storageUrl = "gs://" + storageBucket ;
70- Log .d (TAG , "FirestackStorage url " + storageUrl + path );
197+ Log .d (TAG , "Storage url " + storageUrl + path );
71198 final StorageReference storageRef = storage .getReferenceFromUrl (storageUrl );
72199 final StorageReference fileRef = storageRef .child (path );
73200
@@ -90,16 +217,7 @@ public void onSuccess(Uri uri) {
90217 public void onSuccess (final StorageMetadata storageMetadata ) {
91218 Log .d (TAG , "getMetadata success " + storageMetadata );
92219
93- WritableMap metadata = Arguments .createMap ();
94- metadata .putString ("getBucket" , storageMetadata .getBucket ());
95- metadata .putString ("getName" , storageMetadata .getName ());
96- metadata .putDouble ("sizeBytes" , storageMetadata .getSizeBytes ());
97- metadata .putDouble ("created_at" , storageMetadata .getCreationTimeMillis ());
98- metadata .putDouble ("updated_at" , storageMetadata .getUpdatedTimeMillis ());
99- metadata .putString ("md5hash" , storageMetadata .getMd5Hash ());
100- metadata .putString ("encoding" , storageMetadata .getContentEncoding ());
101-
102- res .putMap ("metadata" , metadata );
220+ res .putMap ("metadata" , getMetadataAsMap (storageMetadata ));
103221 res .putString ("name" , storageMetadata .getName ());
104222 res .putString ("url" , storageMetadata .getDownloadUrl ().toString ());
105223 callback .invoke (null , res );
@@ -109,7 +227,8 @@ public void onSuccess(final StorageMetadata storageMetadata) {
109227 @ Override
110228 public void onFailure (@ NonNull Exception exception ) {
111229 Log .e (TAG , "Failure in download " + exception );
112- callback .invoke (makeErrorPayload (1 , exception ));
230+ final int errorCode = 1 ;
231+ callback .invoke (makeErrorPayload (errorCode , exception ));
113232 }
114233 });
115234
@@ -129,6 +248,18 @@ public void onFailure(@NonNull Exception exception) {
129248 });
130249 }
131250
251+ private WritableMap getMetadataAsMap (StorageMetadata storageMetadata ) {
252+ WritableMap metadata = Arguments .createMap ();
253+ metadata .putString ("getBucket" , storageMetadata .getBucket ());
254+ metadata .putString ("getName" , storageMetadata .getName ());
255+ metadata .putDouble ("sizeBytes" , storageMetadata .getSizeBytes ());
256+ metadata .putDouble ("created_at" , storageMetadata .getCreationTimeMillis ());
257+ metadata .putDouble ("updated_at" , storageMetadata .getUpdatedTimeMillis ());
258+ metadata .putString ("md5hash" , storageMetadata .getMd5Hash ());
259+ metadata .putString ("encoding" , storageMetadata .getContentEncoding ());
260+ return metadata ;
261+ }
262+
132263 // STORAGE
133264 @ ReactMethod
134265 public void uploadFile (final String urlStr , final String name , final String filepath , final ReadableMap metadata , final Callback callback ) {
@@ -191,9 +322,9 @@ public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
191322
192323 if (progress >= 0 ) {
193324 WritableMap data = Arguments .createMap ();
194- data .putString ("eventName" , "upload_progress" );
325+ data .putString ("eventName" , STORAGE_UPLOAD_PROGRESS );
195326 data .putDouble ("progress" , progress );
196- Utils .sendEvent (getReactApplicationContext (), "upload_progress" , data );
327+ Utils .sendEvent (getReactApplicationContext (), STORAGE_UPLOAD_PROGRESS , data );
197328 }
198329 }
199330 })
@@ -204,13 +335,14 @@ public void onPaused(UploadTask.TaskSnapshot taskSnapshot) {
204335 StorageMetadata d = taskSnapshot .getMetadata ();
205336 String bucket = d .getBucket ();
206337 WritableMap data = Arguments .createMap ();
207- data .putString ("eventName" , "upload_paused" );
338+ data .putString ("eventName" , STORAGE_UPLOAD_PAUSED );
208339 data .putString ("ref" , bucket );
209- Utils .sendEvent (getReactApplicationContext (), "upload_paused" , data );
340+ Utils .sendEvent (getReactApplicationContext (), STORAGE_UPLOAD_PAUSED , data );
210341 }
211342 });
212343 } catch (Exception ex ) {
213- callback .invoke (makeErrorPayload (2 , ex ));
344+ final int errorCode = 2 ;
345+ callback .invoke (makeErrorPayload (errorCode , ex ));
214346 }
215347 }
216348
@@ -221,7 +353,8 @@ public void getRealPathFromURI(final String uri, final Callback callback) {
221353 callback .invoke (null , path );
222354 } catch (Exception ex ) {
223355 ex .printStackTrace ();
224- callback .invoke (makeErrorPayload (1 , ex ));
356+ final int errorCode = 1 ;
357+ callback .invoke (makeErrorPayload (errorCode , ex ));
225358 }
226359 }
227360
0 commit comments