Преглед на файлове

Add #19 android file API implementation

Ben Hsieh преди 8 години
родител
ревизия
f99fad91ff
променени са 2 файла, в които са добавени 270 реда и са изтрити 21 реда
  1. 33
    15
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java
  2. 237
    6
      src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java

+ 33
- 15
src/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java Целия файл

@@ -46,26 +46,44 @@ public class RNFetchBlob extends ReactContextBaseJavaModule {
46 46
     @ReactMethod
47 47
     public void getEnvironmentDirs(Callback callback) {
48 48
 
49
-        WritableArray results = Arguments.createArray();
50 49
         ReactApplicationContext ctx = this.getReactApplicationContext();
51
-        callback.invoke(
52
-                String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)),
53
-                String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)),
54
-                String.valueOf(ctx.getFilesDir()),
55
-                String.valueOf(ctx.getCacheDir()),
56
-                String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)),
57
-                String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM))
58
-        );
50
+        RNFetchBlobFS.getSystemfolders(ctx, callback);
51
+
59 52
     }
60 53
 
61 54
     @ReactMethod
62 55
     public void unlink(String path, Callback callback) {
63
-        try {
64
-            new File(RNFetchBlobFS.getTmpPath(this.getReactApplicationContext(), path)).delete();
65
-            callback.invoke(null);
66
-        } catch(Exception err) {
67
-            callback.invoke("Failed to remove file or directory at " + path);
68
-        }
56
+        RNFetchBlobFS.unlink(path, callback);
57
+    }
58
+
59
+    @ReactMethod
60
+    public void exists(String path, Callback callback) {
61
+        RNFetchBlobFS.exists(path, callback);
62
+    }
63
+
64
+    @ReactMethod
65
+    public void cp(String path, String dest, Callback callback) {
66
+        RNFetchBlobFS.cp(path, dest, callback);
67
+    }
68
+
69
+    @ReactMethod
70
+    public void mv(String path, String dest, Callback callback) {
71
+        RNFetchBlobFS.mv(path, dest, callback);
72
+    }
73
+
74
+    @ReactMethod
75
+    public void ls(String path, Callback callback) {
76
+        RNFetchBlobFS.ls(path, callback);
77
+    }
78
+
79
+    @ReactMethod
80
+    public void writeStream(String path, String encode, boolean append, Callback callback) {
81
+        new RNFetchBlobFS(this.getReactApplicationContext()).writeStream(path, encode, append, callback);
82
+    }
83
+
84
+    @ReactMethod
85
+    public void writeChunk(String streamId, String data, Callback callback) {
86
+        RNFetchBlobFS.writeChunk(streamId, data, callback);
69 87
     }
70 88
 
71 89
     @ReactMethod

+ 237
- 6
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java Целия файл

@@ -4,6 +4,7 @@ import android.os.AsyncTask;
4 4
 import android.os.Environment;
5 5
 
6 6
 import com.facebook.react.bridge.Arguments;
7
+import com.facebook.react.bridge.Callback;
7 8
 import com.facebook.react.bridge.ReactApplicationContext;
8 9
 import com.facebook.react.bridge.WritableMap;
9 10
 import com.facebook.react.modules.core.DeviceEventManagerModule;
@@ -11,6 +12,15 @@ import com.loopj.android.http.Base64;
11 12
 
12 13
 import java.io.File;
13 14
 import java.io.FileInputStream;
15
+import java.io.FileOutputStream;
16
+import java.io.IOException;
17
+import java.io.InputStream;
18
+import java.io.OutputStream;
19
+import java.nio.charset.Charset;
20
+import java.nio.charset.StandardCharsets;
21
+import java.util.HashMap;
22
+import java.util.Map;
23
+import java.util.UUID;
14 24
 
15 25
 import cz.msebera.android.httpclient.util.EncodingUtils;
16 26
 
@@ -21,17 +31,50 @@ public class RNFetchBlobFS {
21 31
 
22 32
     ReactApplicationContext mCtx;
23 33
     DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
24
-
25
-    static public String getTmpPath(ReactApplicationContext ctx, String taskId) {
26
-        return ctx.getFilesDir() + "/RNFetchBlobTmp_" + taskId;
27
-    }
28
-
34
+    String encoding = "base64";
35
+    boolean append = false;
36
+    FileOutputStream writeStreamInstance = null;
37
+    static HashMap<String, RNFetchBlobFS> fileStreams = new HashMap<>();
29 38
 
30 39
     RNFetchBlobFS(ReactApplicationContext ctx) {
31 40
         this.mCtx = ctx;
32 41
         this.emitter = ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
33 42
     }
34 43
 
44
+    /**
45
+     * Static method that returns system folders to JS context
46
+     * @param ctx   React Native application context
47
+     * @param callback  Javascript callback function
48
+     */
49
+    static public void getSystemfolders(ReactApplicationContext ctx, Callback callback) {
50
+        callback.invoke(
51
+                // document folder
52
+                String.valueOf(ctx.getFilesDir()),
53
+                // cache folder
54
+                String.valueOf(ctx.getCacheDir()),
55
+                // SD card folder
56
+                String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)),
57
+                // Download folder
58
+                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
59
+        );
60
+    }
61
+
62
+    /**
63
+     * Static method that returns a temp file path
64
+     * @param ctx   React Native application context
65
+     * @param taskId    An unique string for identify
66
+     * @return
67
+     */
68
+    static public String getTmpPath(ReactApplicationContext ctx, String taskId) {
69
+        return ctx.getFilesDir() + "/RNFetchBlobTmp_" + taskId;
70
+    }
71
+
72
+    /**
73
+     * Create a file stream for read
74
+     * @param path  File stream target path
75
+     * @param encoding  File stream decoder, should be one of `base64`, `utf8`, `ascii`
76
+     * @param bufferSize    Buffer size of read stream, default to 4096 (4095 when encode is `base64`)
77
+     */
35 78
     public void readStream( String path, String encoding, int bufferSize) {
36 79
         AsyncTask<String, Integer, Integer> task = new AsyncTask<String, Integer, Integer>() {
37 80
             @Override
@@ -42,7 +85,7 @@ public class RNFetchBlobFS {
42 85
                 String eventName = "RNFetchBlobStream+" + path;
43 86
                 try {
44 87
 
45
-                    int chunkSize = encoding.equalsIgnoreCase("base64") ? 4098 : 4096;
88
+                    int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096;
46 89
                     if(bufferSize > 0)
47 90
                         chunkSize = bufferSize;
48 91
                     FileInputStream fs = new FileInputStream(new File(path));
@@ -84,6 +127,193 @@ public class RNFetchBlobFS {
84 127
         task.execute(path, encoding, String.valueOf(bufferSize));
85 128
     }
86 129
 
130
+    /**
131
+     * Create a write stream and store its instance in RNFetchBlobFS.fileStreams
132
+     * @param path  Target file path
133
+     * @param encoding Should be one of `base64`, `utf8`, `ascii`
134
+     * @param append    Flag represents if the file stream overwrite existing content
135
+     * @param callback
136
+     */
137
+    public void writeStream(String path, String encoding, boolean append, Callback callback) {
138
+        File dest = new File(path);
139
+        if(!dest.exists() || dest.isDirectory()) {
140
+            callback.invoke("target path `" + path + "` may not exists or it's a folder");
141
+            return;
142
+        }
143
+        try {
144
+            OutputStream fs = new FileOutputStream(path, append);
145
+            this.encoding = encoding;
146
+            this.append = append;
147
+            String streamId = UUID.randomUUID().toString();
148
+            RNFetchBlobFS.fileStreams.put(streamId, this);
149
+            callback.invoke(null, streamId);
150
+        } catch(Exception err) {
151
+            callback.invoke("failed to create write stream at path `"+path+"` "+ err.getLocalizedMessage());
152
+        }
153
+
154
+    }
155
+
156
+    /**
157
+     * Write a chunk of data into a file stream.
158
+     * @param streamId File stream ID
159
+     * @param data  Data chunk in string format
160
+     * @param callback JS context callback
161
+     */
162
+    static void writeChunk(String streamId, String data, Callback callback) {
163
+
164
+        RNFetchBlobFS fs = fileStreams.get(streamId);
165
+        FileOutputStream stream = fs.writeStreamInstance;
166
+        byte [] chunk;
167
+        if(fs.encoding.equalsIgnoreCase("ascii")) {
168
+            chunk = data.getBytes(Charset.forName("US-ASCII"));
169
+        }
170
+        else if(fs.encoding.equalsIgnoreCase("base64")) {
171
+            chunk = Base64.decode(data, 0);
172
+        }
173
+        else if(fs.encoding.equalsIgnoreCase("utf8")) {
174
+            chunk = data.getBytes(Charset.forName("UTF-8"));
175
+        }
176
+        else {
177
+            chunk = data.getBytes(Charset.forName("US-ASCII"));
178
+        }
179
+        try {
180
+            stream.write(chunk);
181
+            callback.invoke(null);
182
+        } catch (IOException e) {
183
+            callback.invoke(e.getLocalizedMessage());
184
+        }
185
+    }
186
+
187
+    /**
188
+     * Close file stream by ID
189
+     * @param streamId Stream ID
190
+     * @param callback JS context callback
191
+     */
192
+    static void closeStream(String streamId, Callback callback) {
193
+        try {
194
+            RNFetchBlobFS fs = fileStreams.get(streamId);
195
+            FileOutputStream stream = fs.writeStreamInstance;
196
+            stream.close();
197
+            stream = null;
198
+            fileStreams.remove(streamId);
199
+        } catch(Exception err) {
200
+            callback.invoke(err.getLocalizedMessage());
201
+        }
202
+    }
203
+
204
+    /**
205
+     * Unlink file at path
206
+     * @param path  Path of target
207
+     * @param callback  JS context callback
208
+     */
209
+    static void unlink(String path, Callback callback) {
210
+        try {
211
+            boolean success = new File(path).delete();
212
+            callback.invoke(success);
213
+        } catch(Exception err) {
214
+            if(err != null)
215
+            callback.invoke(err.getLocalizedMessage());
216
+        }
217
+    }
218
+
219
+    /**
220
+     * Copy file to destination path
221
+     * @param path Source path
222
+     * @param dest Target path
223
+     * @param callback  JS context callback
224
+     */
225
+    static void cp(String path, String dest, Callback callback) {
226
+        InputStream in = null;
227
+        OutputStream out = null;
228
+
229
+        try {
230
+
231
+            String destFolder = new File(dest).getPath();
232
+            if(!new File(path).exists()) {
233
+                callback.invoke("source file at path`" + path + "` not exists");
234
+                return;
235
+            }
236
+            if(!new File(destFolder).exists())
237
+                new File(destFolder).mkdir();
238
+            if(!new File(dest).exists())
239
+                new File(dest).createNewFile();
240
+
241
+            in = new FileInputStream(path);
242
+            out = new FileOutputStream(dest);
243
+
244
+            byte[] buf = new byte[1024];
245
+            int len;
246
+            while ((len = in.read(buf)) > 0) {
247
+                out.write(buf, 0, len);
248
+            }
249
+            in.close();
250
+            out.close();
251
+            callback.invoke(null);
252
+        } catch (Exception err) {
253
+            if(err != null)
254
+                callback.invoke(err.getLocalizedMessage());
255
+        } finally {
256
+            try {
257
+                in.close();
258
+            } catch (IOException e) {
259
+                callback.invoke(e.getLocalizedMessage());
260
+            }
261
+            try {
262
+                out.close();
263
+            } catch (IOException e) {
264
+                callback.invoke(e.getLocalizedMessage());
265
+            }
266
+        }
267
+    }
268
+
269
+    /**
270
+     * Move file
271
+     * @param path Source file path
272
+     * @param dest Destination file path
273
+     * @param callback JS context callback
274
+     */
275
+    static void mv(String path, String dest, Callback callback) {
276
+        File src = new File(path);
277
+        if(!src.exists()) {
278
+            callback.invoke("source file at path `" + path + "` does not exists");
279
+            return;
280
+        }
281
+        src.renameTo(new File(dest));
282
+        callback.invoke(null);
283
+    }
284
+
285
+    /**
286
+     * Check if the path exists, also check if it is a folder when exists.
287
+     * @param path Path to check
288
+     * @param callback  JS context callback
289
+     */
290
+    static void exists(String path, Callback callback) {
291
+        boolean exist = new File(path).exists();
292
+        boolean isDir = false;
293
+        if(exist)
294
+         isDir = new File(path).isDirectory();
295
+        callback.invoke(exist, isDir);
296
+    }
297
+
298
+    /**
299
+     * List content of folder
300
+     * @param path Target folder
301
+     * @param callback  JS context callback
302
+     */
303
+    static void ls(String path, Callback callback) {
304
+        File src = new File(path);
305
+        if(!src.exists() || !src.isDirectory())
306
+            callback.invoke(null);
307
+        String [] files = new File(path).list();
308
+        callback.invoke(files);
309
+    }
310
+
311
+    /**
312
+     * Private method for emit read stream event.
313
+     * @param streamName    ID of the read stream
314
+     * @param event Event name, `data`, `end`, `error`, etc.
315
+     * @param data  Event data
316
+     */
87 317
     void emitStreamEvent(String streamName, String event, String data) {
88 318
         WritableMap eventData = Arguments.createMap();
89 319
         eventData.putString("event", event);
@@ -91,6 +321,7 @@ public class RNFetchBlobFS {
91 321
         this.emitter.emit(streamName, eventData);
92 322
     }
93 323
 
324
+    // TODO : should we remove this ?
94 325
     void emitFSData(String taskId, String event, String data) {
95 326
         WritableMap eventData = Arguments.createMap();
96 327
         eventData.putString("event", event);