Skip to content

Commit 79efa43

Browse files
Nikita Kraevfacebook-github-bot
Nikita Kraev
authored andcommitted
Update ImageEditingManager to use internal storage before falling back to external for cache
Summary: Changelog: [Android] [Changed] - Internal storage now will be preferred for caching images from ImageEditor. Now we try to write to internal cache directory first. If that fails (due to no memory error or other IOException), we attempt to write to external storage (if that is allowed). I introduced additional configuration flag, `allowExternalStorage` which is true by default. And i updated documentation for new behaviour - see `ImageEditor.cropImage(..)` comment. Reviewed By: makovkastar Differential Revision: D19878158 fbshipit-source-id: 7e338ce68f535f74c62b5eecd5a94af7e7396f8b
1 parent 956359b commit 79efa43

File tree

2 files changed

+42
-26
lines changed

2 files changed

+42
-26
lines changed

Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h

+6
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,7 @@ namespace JS {
14161416
JS::NativeImageEditor::OptionsSize size() const;
14171417
folly::Optional<JS::NativeImageEditor::OptionsDisplaySize> displaySize() const;
14181418
NSString *resizeMode() const;
1419+
folly::Optional<bool> allowExternalStorage() const;
14191420

14201421
Options(NSDictionary *const v) : _v(v) {}
14211422
private:
@@ -3490,6 +3491,11 @@ inline NSString *JS::NativeImageEditor::Options::resizeMode() const
34903491
id const p = _v[@"resizeMode"];
34913492
return RCTBridgingToString(p);
34923493
}
3494+
inline folly::Optional<bool> JS::NativeImageEditor::Options::allowExternalStorage() const
3495+
{
3496+
id const p = _v[@"allowExternalStorage"];
3497+
return RCTBridgingToOptionalBool(p);
3498+
}
34933499
inline bool JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig::unmirrorFrontFacingCamera() const
34943500
{
34953501
id const p = _v[@"unmirrorFrontFacingCamera"];

ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageEditingManager.java

+36-26
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ public void cropImage(
158158
String uri, ReadableMap options, final Callback success, final Callback error) {
159159
ReadableMap offset = options.hasKey("offset") ? options.getMap("offset") : null;
160160
ReadableMap size = options.hasKey("size") ? options.getMap("size") : null;
161+
boolean allowExternalStorage =
162+
options.hasKey("allowExternalStorage") ? options.getBoolean("allowExternalStorage") : true;
163+
161164
if (offset == null
162165
|| size == null
163166
|| !offset.hasKey("x")
@@ -178,6 +181,7 @@ public void cropImage(
178181
(int) offset.getDouble("y"),
179182
(int) size.getDouble("width"),
180183
(int) size.getDouble("height"),
184+
allowExternalStorage,
181185
success,
182186
error);
183187
if (options.hasKey("displaySize")) {
@@ -195,6 +199,7 @@ private static class CropTask extends GuardedAsyncTask<Void, Void> {
195199
final int mY;
196200
final int mWidth;
197201
final int mHeight;
202+
final boolean mAllowExternalStorage;
198203
int mTargetWidth = 0;
199204
int mTargetHeight = 0;
200205
final Callback mSuccess;
@@ -207,6 +212,7 @@ private CropTask(
207212
int y,
208213
int width,
209214
int height,
215+
boolean allowExternalStorage,
210216
Callback success,
211217
Callback error) {
212218
super(context);
@@ -220,6 +226,7 @@ private CropTask(
220226
mY = y;
221227
mWidth = width;
222228
mHeight = height;
229+
mAllowExternalStorage = allowExternalStorage;
223230
mSuccess = success;
224231
mError = error;
225232
}
@@ -267,8 +274,17 @@ protected void doInBackgroundGuarded(Void... params) {
267274
throw new IOException("Could not determine MIME type");
268275
}
269276

270-
File tempFile = createTempFile(mContext, mimeType);
271-
writeCompressedBitmapToFile(cropped, mimeType, tempFile);
277+
File tempFile;
278+
try {
279+
tempFile = writeBitmapToInternalCache(mContext, cropped, mimeType);
280+
} catch (Exception e) {
281+
if (mAllowExternalStorage) {
282+
tempFile = writeBitmapToExternalCache(mContext, cropped, mimeType);
283+
} else {
284+
throw new SecurityException(
285+
"We couldn't create file in internal cache and external cache is disabled. Did you forget to pass allowExternalStorage=true?");
286+
}
287+
}
272288

273289
if (mimeType.equals("image/jpeg")) {
274290
copyExif(mContext, Uri.parse(mUri), tempFile);
@@ -455,42 +471,36 @@ private static Bitmap.CompressFormat getCompressFormatForType(String type) {
455471
return Bitmap.CompressFormat.JPEG;
456472
}
457473

474+
private static File writeBitmapToInternalCache(Context context, Bitmap cropped, String mimeType)
475+
throws IOException {
476+
File tempFile = createTempFile(context.getCacheDir(), mimeType);
477+
writeCompressedBitmapToFile(cropped, mimeType, tempFile);
478+
return tempFile;
479+
}
480+
481+
private static File writeBitmapToExternalCache(Context context, Bitmap cropped, String mimeType)
482+
throws IOException {
483+
File tempFile = createTempFile(context.getExternalCacheDir(), mimeType);
484+
writeCompressedBitmapToFile(cropped, mimeType, tempFile);
485+
return tempFile;
486+
}
487+
458488
private static void writeCompressedBitmapToFile(Bitmap cropped, String mimeType, File tempFile)
459489
throws IOException {
460490
OutputStream out = new FileOutputStream(tempFile);
461-
try {
462-
cropped.compress(getCompressFormatForType(mimeType), COMPRESS_QUALITY, out);
463-
} finally {
464-
if (out != null) {
465-
out.close();
466-
}
467-
}
491+
cropped.compress(getCompressFormatForType(mimeType), COMPRESS_QUALITY, out);
468492
}
469493

470494
/**
471-
* Create a temporary file in the cache directory on either internal or external storage,
472-
* whichever is available and has more free space.
495+
* Create a temporary file in internal / external storage to use for image scaling and caching.
473496
*
474497
* @param mimeType the MIME type of the file to create (image/*)
475498
*/
476-
private static File createTempFile(Context context, @Nullable String mimeType)
499+
private static File createTempFile(@Nullable File cacheDir, @Nullable String mimeType)
477500
throws IOException {
478-
File externalCacheDir = context.getExternalCacheDir();
479-
File internalCacheDir = context.getCacheDir();
480-
File cacheDir;
481-
if (externalCacheDir == null && internalCacheDir == null) {
501+
if (cacheDir == null) {
482502
throw new IOException("No cache directory available");
483503
}
484-
if (externalCacheDir == null) {
485-
cacheDir = internalCacheDir;
486-
} else if (internalCacheDir == null) {
487-
cacheDir = externalCacheDir;
488-
} else {
489-
cacheDir =
490-
externalCacheDir.getFreeSpace() > internalCacheDir.getFreeSpace()
491-
? externalCacheDir
492-
: internalCacheDir;
493-
}
494504
return File.createTempFile(TEMP_FILE_PREFIX, getFileExtensionForType(mimeType), cacheDir);
495505
}
496506

0 commit comments

Comments
 (0)