Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

use temp image file in cacheDir to allow picking from remote resources #513

Merged

Conversation

gbaccetta
Copy link
Contributor

imagePicker caused flutter app to crash when picking image from a remote source.
Using a temp file to store the inputStream obtained from the content provider solve the problem.
It actually use a temp file in the cachedDir named cachedImage.jpg.

@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed (or fixed any issues), please reply here (e.g. I signed it!) and we'll verify it.


What to do if you already signed the CLA

Individual signers
Corporate signers

@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again. If the bot doesn't comment, it means it doesn't think anything has changed.

@gbaccetta
Copy link
Contributor Author

gbaccetta commented Apr 30, 2018 via email

Copy link
Contributor

@mravn-google mravn-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

try {
InputStream inputStream = null;
OutputStream output = null;
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use try-with-resources. It is too hard to get this code correct without.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't resource management only for API 19+ and flutter for 16+ ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, sorry. In that case, you need to wrap your calls to close in try-catch blocks. Otherwise we risk not actually closing the input stream, as well as exiting abnormally from a finally block.

OutputStream output = null;
try {
inputStream = activity.getContentResolver().openInputStream(data.getData());
File file = new File(activity.getCacheDir(), "cachedImage.jpg");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if that file already exists?
When is the file cleaned up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guessed that for the cleaning since it is the cacheDir and I was trying to always use the same file name that was ok. In that sense imageResizer is even worse since it create a scaled image version in the external directory where the original file is, leaving the user with two copies of it. However you are right about the file already existing

File file = new File(activity.getCacheDir(), "cachedImage.jpg");
output = new FileOutputStream(file);
if (inputStream != null) {
byte[] buffer = new byte[4 * 1024]; // or other buffer size
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid comments that convey only uncertainty. There is nothing inherently wrong with a 4K buffer.

@@ -300,8 +304,36 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {

private void handleChoosePictureResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
String path = fileUtils.getPathFromUri(activity, data.getData());
handleResult(path);
//String path = fileUtils.getPathFromUri(activity, data.getData());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove unneeded code rather than put it in a comment.

If FileUtils is no longer used by the plugin, it should be removed as well.

handleResult(path);
//String path = fileUtils.getPathFromUri(activity, data.getData());

//creating a temp file from uri to allow reading pictures from remote sources
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if it isn't remote, do we then copy the file needlessly?

Please format comments as whole sentences:

// Create a temporary file from ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well we can keep using FileUtils for local path and create temporary file only for remoteResources when path is null in the same way as done for the camera intent

@mravn-google mravn-google self-assigned this May 1, 2018
@mravn-google
Copy link
Contributor

The commit appears to have been made using a different author name or email than that used for CLA signing. You probably have to amend your commit.

@roughike
Copy link
Contributor

roughike commented May 1, 2018

@mravn-google @gbaccetta This PR breaks two tests (found in example/android/app/src/test).

There's no CI integration for running these tests just yet; @goderbauer requested me to modify the Travis script in this comment in #461. I haven't had time to look into it, and honestly have no idea how to do / test it.

(The tests can be run from the command line by running cd packages/image_picker/example/android && ./gradlew testDebugUnitTest.)

@gbaccetta gbaccetta force-pushed the image_picker_fix_remote_sources_error branch 2 times, most recently from a9a10d4 to 036ccdf Compare May 1, 2018 12:58
@googlebot
Copy link

CLAs look good, thanks!

@googlebot googlebot added cla: yes and removed cla: no labels May 1, 2018
@goderbauer goderbauer closed this May 1, 2018
@goderbauer goderbauer reopened this May 1, 2018
@goderbauer goderbauer closed this May 1, 2018
@goderbauer goderbauer reopened this May 1, 2018
@goderbauer
Copy link
Member

@roughike Travis ran the image_picker java tests on this PR and they're still passing. Is that unexpected?

@roughike
Copy link
Contributor

roughike commented May 1, 2018

@goderbauer Java tests should now pass.

The Java tests failed when there was only the first commit, c808da73cfd, on this PR. The later commit, 036ccdf1f04, fixed the failing tests, making all test cases pass again. Just confirmed this locally to be extra sure.

So everything is working as expected!

@goderbauer
Copy link
Member

@gbaccetta Does this by any chance also fix flutter/flutter#17165?

@gbaccetta
Copy link
Contributor Author

@goderbauer yes it fix also that problem, that's why I looked for this solution. For the formating problem I could not change my Android studio indentention from 4 to 2 characters... sorry about that

@gbaccetta
Copy link
Contributor Author

ok, I should have fixed the formatting in last commit

@roughike
Copy link
Contributor

roughike commented May 2, 2018

@gbaccetta You can format all Dart, Java and Objective C files by running pub global run flutter_plugin_tools format in the plugins directory. I also have indentation with 4 spaces in my Android Studio, but I always run that command before committing anything. :)

See here: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md

@gbaccetta gbaccetta force-pushed the image_picker_fix_remote_sources_error branch 2 times, most recently from 41f091f to 36be00c Compare May 2, 2018 10:49
@gbaccetta
Copy link
Contributor Author

Check still failed even after correcting the format. Could not understand why this time. I'm sorry this is the first time I 'm trying to contribute to Google Open Source.

@roughike that command for me fails with this from the plugin directory:

Error: Cannot find a "packages" sub-directory
pub finished with exit code 1

and with this from the root directory:

Error: No pubspec.yaml file found.
This command should be run from the root of your Flutter project.
Do not run this command from the root of your git clone of Flutter.

@roughike
Copy link
Contributor

roughike commented May 2, 2018

No worries!

Let's say you have cloned this repo in C:/Documents/my-github-contributions/plugins. Assuming that opening a new terminal window starts you at C:/, here's what you do:

cd Documents
cd my-github-contributions
cd plugins # this is the directory you want to be in

# Now we're inside the correct directory, so we can use the plugin tools.
pub global run flutter_plugin_tools format

handleResult(path);
//Path may be null if, for example, source is remote.
if (path == null) {
InputStream inputStream = null;
Copy link
Contributor

@roughike roughike May 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a drive-by comment: I would at least extract this logic into a separate method (called getRemotePathFromUri() for example) to make the code a bit clearer.

Maybe even move this logic to FileUtils class if we go crazy. If the Uri has a distinctive scheme or authority, it could be added as an else if at Line 90 in the FileUtils class.

Something like:

FileUtils.java (starting from line 90)

...
    } else if (isRemoteFile(uri)) {
        // Move the logic for getting remote path from the uri here.
        // Then return the resolved path.
    } 
...

Disclaimer: I'm not in the Flutter team and you can just ignore this feedback if you want. ¯\(ツ)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem is the authority is not distintive.
For example local file from google photos has the same authority then remote file.
However the local file path is correctly retrieved but not the remote.
That's why I used the logic as a fallback cause this always works but need to create a temp file... Hence the idea was to say: let's try to retrieve the path in every manner we may know of and fallback to inputstream and tempFile if those methods fail. It allows to keep the plugin to work even if a provider change its distintive authority

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem is the authority is not distinctive.

How about the scheme then?

If not, I would still personally move the logic to FileUtils. In the very end of the getPathFromUri method, we could return the path from remote Uri if it can be resolved. If not, then just return null like it currently does.

OutputStream outputStream = null;
try {
inputStream = activity.getContentResolver().openInputStream(data.getData());
File file = File.createTempFile("cachedImage", "jpg", activity.getCacheDir());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking if cachedImage.jpg is a too generic name for an image to be stored in a cache directory. There could be a chance we're overwriting some other image by accident. Would it make sense to use something less common, like flutter_image_picker_cached_remote_image.jpg for example?

UUIDs would be another option, but that could blow up the application's cache directory over time since images can get quite big. This might potentially give end users a bad impression of the app that uses this plugin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roughike normay using createTempFile automatic append a random numeric string to the end of the name so that files will be "cachedImage-123456789.jpg" or something similar. I did not find of a nice way of automatically deleting the file created in the cacheDir

@googlebot
Copy link

CLAs look good, thanks!

@@ -63,7 +63,7 @@
* <p>C) User cancels picking an image. Finish with null result.
*/
public class ImagePickerDelegate
implements PluginRegistry.ActivityResultListener,
implements PluginRegistry.ActivityResultListener,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These white space changes seem to be at odds with our CI's formatting requirements. Please revert.

outputStream.write(buffer, 0, read);
}
outputStream.flush();
return file.getPath();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are returning a file path here even if there are exceptions thrown while closing the streams. We should only return a file path if all the I/O completes normally.

String getPathFromUri(final Context context, final Uri uri) {
//Try to retrieve path on device
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments add little value; the code already speaks for itself.

@gbaccetta gbaccetta force-pushed the image_picker_fix_remote_sources_error branch from 8ab48f3 to d7bbf7f Compare June 11, 2018 13:58
@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again. If the bot doesn't comment, it means it doesn't think anything has changed.

@gbaccetta gbaccetta force-pushed the image_picker_fix_remote_sources_error branch 2 times, most recently from 9fe8c24 to fbdd8c7 Compare June 11, 2018 14:47
@googlebot
Copy link

CLAs look good, thanks!

…e, rebase into master and logic moved to FileUtils)
@gbaccetta gbaccetta force-pushed the image_picker_fix_remote_sources_error branch from fbdd8c7 to 38ea750 Compare June 11, 2018 15:22
@gbaccetta
Copy link
Contributor Author

Finally starting from scratch ( new branch from actual master + force push to clean all the mess) seems to have worked. Anyway formatting is really hard to achieve since pub global run flutter_plugin_tools format does not seem to work on my pc.

@mravn-google
Copy link
Contributor

@gbaccetta Do you want to work on the last review comment, or would you prefer us taking it from here?

@gbaccetta
Copy link
Contributor Author

gbaccetta commented Jun 13, 2018

@mravn-google you can take over. However, as I see it, in case of exception the code return null and not the filePath. Unless I missinterpreted something

@mravn-google
Copy link
Contributor

@gbaccetta Thanks, I'll make a small tweak in a separate PR and then publish.

@SkullEnemyX
Copy link

Somebody please find a fix to the problem which is making the app crash if we choose an image using image_picker from google photos but works totally fine if the image is picked from the local gallery. I am in dire need for the solution.

@roughike
Copy link
Contributor

@SkullEnemyX I believe this PR is exactly that fix.

@SkullEnemyX
Copy link

I am unable to understand what to do exactly as the method is not clear (PS: I am a beginner in app development and flutter)

@roughike
Copy link
Contributor

That's fine!

As far as I understand, a fix for your crash with Google Photos was just merged in this pull request. Is the problem still surfacing with version 0.4.5 of this plugin?

@pojoba02
Copy link

@gbaccetta @goderbauer @roughike @mravn-google This issue is completely not fixed, it still does exist, Guess is happening in Oreo and above(Pie). App just crashes when uploading images and it never displays the image too.
Please what might be causing it?
FYI am using version 0.4.10 of the image Picker

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants