diff --git a/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java b/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java index 276621273..ba5eef5e1 100644 --- a/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java +++ b/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java @@ -432,29 +432,29 @@ public void createMediaFile(ReadableMap filedata, String mt, Promise promise) { if (mt == null) promise.reject("ReactNativeBlobUtil.createMediaFile", "invalid mediatype"); FileDescription file = new FileDescription(filedata.getString("name"), filedata.getString("mimeType"), filedata.getString("parentFolder")); - Uri res = ReactNativeBlobUtilMediaCollection.createNewMediaFile(file, ReactNativeBlobUtilMediaCollection.MediaType.valueOf(mt)); + Uri res = ReactNativeBlobUtilMediaCollection.createNewMediaFile(file, ReactNativeBlobUtilMediaCollection.MediaType.valueOf(mt), this.getReactApplicationContext()); if (res != null) promise.resolve(res.toString()); else promise.reject("ReactNativeBlobUtil.createMediaFile", "File could not be created"); } - @RequiresApi(api = Build.VERSION_CODES.Q) @ReactMethod public void writeToMediaFile(String fileUri, String path, Promise promise) { boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(Uri.parse(fileUri), path, promise); if(res) promise.resolve("Success"); } + @RequiresApi(api = Build.VERSION_CODES.Q) @ReactMethod public void copyToInternal(String contentUri, String destpath, Promise promise) { ReactNativeBlobUtilMediaCollection.copyToInternal(Uri.parse(contentUri), destpath, promise); } + @RequiresApi(api = Build.VERSION_CODES.Q) @ReactMethod public void getBlob(String contentUri, String encoding, Promise promise) { ReactNativeBlobUtilMediaCollection.getBlob(Uri.parse(contentUri), encoding, promise); } - @RequiresApi(api = Build.VERSION_CODES.Q) @ReactMethod public void copyToMediaStore(ReadableMap filedata, String mt, String path, Promise promise) { if (!(filedata.hasKey("name") && filedata.hasKey("parentFolder") && filedata.hasKey("mimeType"))) { @@ -471,7 +471,7 @@ public void copyToMediaStore(ReadableMap filedata, String mt, String path, Promi } FileDescription file = new FileDescription(filedata.getString("name"), filedata.getString("mimeType"), filedata.getString("parentFolder")); - Uri fileuri = ReactNativeBlobUtilMediaCollection.createNewMediaFile(file, ReactNativeBlobUtilMediaCollection.MediaType.valueOf(mt)); + Uri fileuri = ReactNativeBlobUtilMediaCollection.createNewMediaFile(file, ReactNativeBlobUtilMediaCollection.MediaType.valueOf(mt), this.getReactApplicationContext()); if (fileuri == null) { promise.reject("ReactNativeBlobUtil.createMediaFile", "File could not be created"); @@ -479,7 +479,6 @@ public void copyToMediaStore(ReadableMap filedata, String mt, String path, Promi } boolean res = ReactNativeBlobUtilMediaCollection.writeToMediaFile(fileuri, path, promise); - if(res) promise.resolve(fileuri.toString()); } diff --git a/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java b/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java index d19ee0e62..eed40d083 100644 --- a/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java +++ b/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java @@ -44,6 +44,75 @@ class ReactNativeBlobUtilFS { this.emitter = ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); } + /** + * Write string with encoding to file (used for mediastore) + * + * @param path Destination file path. + * @param encoding Encoding of the string. + * @param data Array passed from JS context. + */ + static boolean writeFile(String path, String encoding, String data, final boolean append) { + try { + int written; + File f = new File(path); + File dir = f.getParentFile(); + if (!f.exists()) { + if (dir != null && !dir.exists()) { + if (!dir.mkdirs() && !dir.exists()) { + return false; + } + } + if (!f.createNewFile()) { + return false; + } + } + + // write data from a file + if (encoding.equalsIgnoreCase(ReactNativeBlobUtilConst.DATA_ENCODE_URI)) { + String normalizedData = ReactNativeBlobUtilUtils.normalizePath(data); + File src = new File(normalizedData); + if (!src.exists()) { + return false; + } + byte[] buffer = new byte[10240]; + int read; + written = 0; + FileInputStream fin = null; + FileOutputStream fout = null; + try { + fin = new FileInputStream(src); + fout = new FileOutputStream(f, append); + while ((read = fin.read(buffer)) > 0) { + fout.write(buffer, 0, read); + written += read; + } + } finally { + if (fin != null) { + fin.close(); + } + if (fout != null) { + fout.close(); + } + } + } else { + byte[] bytes = ReactNativeBlobUtilUtils.stringToBytes(data, encoding); + FileOutputStream fout = new FileOutputStream(f, append); + try { + fout.write(bytes); + written = bytes.length; + } finally { + fout.close(); + } + } + return true; + } catch (FileNotFoundException e) { + // According to https://docs.oracle.com/javase/7/docs/api/java/io/FileOutputStream.html + return false; + } catch (Exception e) { + return false; + } + } + /** * Write string with encoding to file * @@ -57,7 +126,6 @@ static void writeFile(String path, String encoding, String data, final boolean a int written; File f = new File(path); File dir = f.getParentFile(); - if (!f.exists()) { if (dir != null && !dir.exists()) { if (!dir.mkdirs() && !dir.exists()) { diff --git a/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java b/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java index 3aa7c3af0..810ad5e61 100644 --- a/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java +++ b/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilMediaCollection.java @@ -10,11 +10,10 @@ import android.provider.MediaStore; import android.util.Base64; -import androidx.annotation.RequiresApi; - import com.ReactNativeBlobUtil.Utils.FileDescription; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.WritableArray; import java.io.File; @@ -62,21 +61,29 @@ private static Uri getMediaUri(MediaType mt) { return res; } - private static String getRelativePath(MediaType mt) { - if (mt == MediaType.Audio) return Environment.DIRECTORY_MUSIC; - if (mt == MediaType.Video) return Environment.DIRECTORY_MOVIES; - if (mt == MediaType.Image) return Environment.DIRECTORY_PICTURES; - if (mt == MediaType.Download) return Environment.DIRECTORY_DOWNLOADS; - return Environment.DIRECTORY_DOWNLOADS; + private static String getRelativePath(MediaType mt, ReactApplicationContext ctx) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (mt == MediaType.Audio) return Environment.DIRECTORY_MUSIC; + if (mt == MediaType.Video) return Environment.DIRECTORY_MOVIES; + if (mt == MediaType.Image) return Environment.DIRECTORY_PICTURES; + if (mt == MediaType.Download) return Environment.DIRECTORY_DOWNLOADS; + return Environment.DIRECTORY_DOWNLOADS; + } else { + if (mt == MediaType.Audio) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_MUSIC); + if (mt == MediaType.Video) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_MOVIES); + if (mt == MediaType.Image) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_PICTURES); + if (mt == MediaType.Download) return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_DOWNLOADS); + return ReactNativeBlobUtilFS.getExternalFilesDirPath(ctx, Environment.DIRECTORY_DOWNLOADS); + } } - public static Uri createNewMediaFile(FileDescription file, MediaType mt) { + public static Uri createNewMediaFile(FileDescription file, MediaType mt, ReactApplicationContext ctx) { // Add a specific media item. Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext(); ContentResolver resolver = appCtx.getContentResolver(); ContentValues fileDetails = new ContentValues(); - String relativePath = getRelativePath(mt); + String relativePath = getRelativePath(mt, ctx); String mimeType = file.mimeType; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -91,12 +98,24 @@ public static Uri createNewMediaFile(FileDescription file, MediaType mt) { // Keeps a handle to the new file's URI in case we need to modify it later. return resolver.insert(mediauri, fileDetails); } else { - File directory = Environment.getExternalStoragePublicDirectory(relativePath); - if (directory.canWrite()) { - File f = new File(relativePath + '/' + file.getFullPath()); + File f = new File(relativePath + file.getFullPath()); + if (true) { if (!f.exists()) { - boolean result = f.mkdirs(); - if (result) return Uri.fromFile(f); + File parent = f.getParentFile(); + if (parent != null && !parent.exists() && !parent.mkdirs()) { + return null; + } + try { + if (f.createNewFile()) ; + { + return Uri.fromFile(f); + } + } catch (IOException ioException) { + return null; + } + + } else { + return Uri.fromFile(f); } } @@ -105,83 +124,84 @@ public static Uri createNewMediaFile(FileDescription file, MediaType mt) { return null; } - @RequiresApi(api = Build.VERSION_CODES.Q) public static boolean writeToMediaFile(Uri fileUri, String data, Promise promise) { - try { - Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext(); - ContentResolver resolver = appCtx.getContentResolver(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + try { + Context appCtx = ReactNativeBlobUtil.RCTContext.getApplicationContext(); + ContentResolver resolver = appCtx.getContentResolver(); - // set pending - ContentValues contentValues = new ContentValues(); - contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1); - resolver.update(fileUri, contentValues, null, null); + // set pending doesn't work right now. We would have to requery for the item + //ContentValues contentValues = new ContentValues(); + //contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1); + //resolver.update(fileUri, contentValues, null, null); - // write data - OutputStream stream = null; - Uri uri = null; + // write data + OutputStream stream = null; + Uri uri = null; - try { - ParcelFileDescriptor descr; try { - assert fileUri != null; - descr = appCtx.getContentResolver().openFileDescriptor(fileUri, "w"); - assert descr != null; - String normalizedData = ReactNativeBlobUtilUtils.normalizePath(data); - File src = new File(normalizedData); - if (!src.exists()) { - promise.reject("ENOENT", "No such file ('" + normalizedData + "')"); + ParcelFileDescriptor descr; + try { + assert fileUri != null; + descr = appCtx.getContentResolver().openFileDescriptor(fileUri, "w"); + assert descr != null; + String normalizedData = ReactNativeBlobUtilUtils.normalizePath(data); + File src = new File(normalizedData); + if (!src.exists()) { + promise.reject("ENOENT", "No such file ('" + normalizedData + "')"); + return false; + } + byte[] buf = new byte[10240]; + int read; + + FileInputStream fin = new FileInputStream(src); + FileOutputStream out = new FileOutputStream(descr.getFileDescriptor()); + + while ((read = fin.read(buf)) > 0) { + out.write(buf, 0, read); + } + + fin.close(); + out.close(); + descr.close(); + } catch (Exception e) { + e.printStackTrace(); + promise.reject(new IOException("Failed to get output stream.")); return false; } - byte[] buf = new byte[10240]; - int read; - int written = 0; - - FileInputStream fin = new FileInputStream(src); - FileOutputStream out = new FileOutputStream(descr.getFileDescriptor()); - while ((read = fin.read(buf)) > 0) { - out.write(buf, 0, read); + //contentValues.clear(); + //contentValues.put(MediaStore.Video.Media.IS_PENDING, 0); + //appCtx.getContentResolver().update(fileUri, contentValues, null, null); + stream = resolver.openOutputStream(fileUri); + if (stream == null) { + promise.reject(new IOException("Failed to get output stream.")); + return false; } - - fin.close(); - out.close(); - descr.close(); - } catch (Exception e) { - e.printStackTrace(); - promise.reject(new IOException("Failed to get output stream.")); + } catch (IOException e) { + // Don't leave an orphan entry in the MediaStore + resolver.delete(uri, null, null); + promise.reject(e); return false; + } finally { + if (stream != null) { + stream.close(); + } } - contentValues.clear(); - contentValues.put(MediaStore.Video.Media.IS_PENDING, 0); - appCtx.getContentResolver().update(fileUri, contentValues, null, null); - stream = resolver.openOutputStream(fileUri); - if (stream == null) { - promise.reject(new IOException("Failed to get output stream.")); - return false; - } + // remove pending + //contentValues = new ContentValues(); + //contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0); + //resolver.update(fileUri, contentValues, null, null); + } catch (IOException e) { - // Don't leave an orphan entry in the MediaStore - resolver.delete(uri, null, null); - promise.reject(e); + promise.reject("ReactNativeBlobUtil.createMediaFile", "Cannot write to file, file might not exist"); return false; - } finally { - if (stream != null) { - stream.close(); - return true; - } } - - // remove pending - contentValues = new ContentValues(); - contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0); - resolver.update(fileUri, contentValues, null, null); - - } catch (IOException e) { - promise.reject("ReactNativeBlobUtil.createMediaFile", "Cannot write to file, file might not exist"); - return false; + return true; + } else { + return ReactNativeBlobUtilFS.writeFile(ReactNativeBlobUtilUtils.normalizePath(fileUri.toString()), ReactNativeBlobUtilConst.DATA_ENCODE_URI, data, false); } - return true; } public static void copyToInternal(Uri contenturi, String destpath, Promise promise) { @@ -190,10 +210,16 @@ public static void copyToInternal(Uri contenturi, String destpath, Promise promi InputStream in = null; OutputStream out = null; + File f = new File((destpath)); - if (!new File(destpath).exists()) { + if (!f.exists()) { try { - boolean result = new File(destpath).createNewFile(); + File parent = f.getParentFile(); + if (parent != null && !parent.exists() && !parent.mkdirs()) { + promise.reject("ReactNativeBlobUtil.copyToInternal: Cannot create parent folders<'" + destpath); + return; + } + boolean result = f.createNewFile(); if (!result) { promise.reject("ReactNativeBlobUtil.copyToInternal: Destination file at '" + destpath + "' already exists"); return; @@ -240,9 +266,7 @@ public static void getBlob(Uri contentUri, String encoding, Promise promise) { ContentResolver resolver = appCtx.getContentResolver(); try { InputStream in = resolver.openInputStream(contentUri); - int length = 0; - - length = in.available(); + int length = in.available(); byte[] bytes = new byte[length]; int bytesRead = in.read(bytes); @@ -260,14 +284,11 @@ public static void getBlob(Uri contentUri, String encoding, Promise promise) { case "ascii": WritableArray asciiResult = Arguments.createArray(); for (byte b : bytes) { - asciiResult.pushInt((int) b); + asciiResult.pushInt(b); } promise.resolve(asciiResult); break; - case "utf8": - promise.resolve(new String(bytes)); - break; - default: + default: // covers utf-8 promise.resolve(new String(bytes)); break; } diff --git a/package.json b/package.json index 35b2e8a7b..bc5ef8252 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-blob-util", - "version": "0.14.0", + "version": "0.14.1", "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.", "main": "index.js", "scripts": {