FileNotFoundException in onActivityResult - java

I am trying to browse only two file-types: images or pdf.
Here is the source:
String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
myPermissions =new MyPermissions(TestDialog.this, 0, permissions);
MyPermissions.EventHandler permHandler = new MyPermissions.EventHandler() {
#Override
public void handle() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("application/pdf");
intent.setType("image/jpeg");
startActivityForResult(intent, 0);
}
};
myPermissions.doIfHasPermissions(permHandler);
Here is a my onActivityResult source:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
String url = data.getData().getPath();
File myFile = new File(url);
Log.e("base64 ", getStringFile(myFile));
}
super.onActivityResult(requestCode, resultCode, data);
}
public String getStringFile(File f) {
InputStream inputStream = null;
String encodedFile = "", lastVal;
try {
inputStream = new FileInputStream(f.getAbsolutePath());
byte[] buffer = new byte[10240];//specify the size to allow
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
Base64OutputStream output64 = new Base64OutputStream(output, Base64.DEFAULT);
while ((bytesRead = inputStream.read(buffer)) != -1) {
output64.write(buffer, 0, bytesRead);
}
output64.close();
encodedFile = output.toString();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
lastVal = encodedFile;
return lastVal;
}
I would like to convert the selected file to Base64, but I get a FileNotFoundException. Does anyone have any idea what am I doing wrong?

Have a look at
Uri uri = data.getData();
Then try to log the value of uri.toString().
You will see that it starts with "content//....".
Do not try to find a file.
Instead of a FileInputStream use an InputStream.
InputStream inputStream = getContentResolver().openInputStream(uri);

I try to browse only two type files,images or pdf
Your code has nothing much to do with files. It uses ACTION_GET_CONTENT, which allows the user to choose a piece of content.
String url = data.getData().getPath();
This line is useless, unless the Uri has a scheme of file. Most likely, it has a scheme of content.
Stop using File and FileInputStream. Instead, get an InputStream from a ContentResolver (from getContentResolver()) and its openInputStream() method. You can pass in the Uri, and you will get an InputStream regardless of whether the Uri scheme is file or content.
Also note that your app is likely to crash with an OutOfMemoryError, except for fairly small files, as you will not have enough heap space to perform this conversion.

Related

Convert android Uri to java File Android Studio

I have an Intent of ACTION_GET_CONTENT in my app and I need to put the picked file ( there will be different files, ppt, doc...) in a java.io File.
I'm able to get the data and put it into a android.net Uri. Is there a way I ca create a java File from this Uri?
I need it to be a file in order to upload it do google drive using the google drive API
This is the code to upload to the drive, I need to convert the uri to a temporary file in order to pass it as the javaFile of this method
public Task<File> uploadFileWithMetadata(java.io.File javaFile, boolean isSlide, #Nullable final String folderId, PostFileHolder postFileHolder) {
return Tasks.call(mExecutor, () -> {
Log.i("upload file", "chegou" );
String convertTo; // string to convert to gworkspace
if(isSlide){
convertTo = TYPE_GOOGLE_SLIDES;
}
else{
convertTo = TYPE_GOOGLE_DOCS;
}
List<String> folder;
if (folderId == null) {
folder = Collections.singletonList("root");
} else {
folder = Collections.singletonList(folderId);
}
File metadata = new File()
.setParents(Collections.singletonList(folderId))
.setName(postFileHolder.getDisplayName())
.setMimeType(convertTo);
Log.i("convert to: ", convertTo );
// the convert to is the mimeType of the file, withg gworkspace it is a gdoc or gslide, with others is the regular mimetype
FileContent mediaContent = new FileContent(postFileHolder.getConvertTo(), javaFile);
Log.i("media content", "chegou" );
// até aqui com gworkspace chega
File uploadedFile = mDriveService.files().create(metadata, mediaContent)
.setFields("id")
.execute();
Log.i("File ID: " , uploadedFile.getId());
return uploadedFile;
});
}
This is my code to get the Uri
case REQUEST_CODE_FILE_PICKER:
// get uri from file picked
Uri url = data.getData();
break;
}
Solved it!
Here's how I did it:
// my uri
Uri fileUri = Uri.parse(postFileHolder.getFileUri());
// create a null InputSream
InputStream iStream = null;
try {
// create a temporary file
File fileToUpload = File.createTempFile("fileToUpload", null, this.getCacheDir());
iStream = getContentResolver().openInputStream(fileUri);
// use function to get the bytes from the created InputStream
byte[] byteData = getBytes(iStream);
convert byteArray to File
FileOutputStream fos = new FileOutputStream(fileToUpload);
fos.write(byteData);
fos.flush();
fos.close();
if(fileToUpload == null){
Log.i("create file", "null");
}
else{
Log.i("create file", "not null: "+ fileToUpload.getTotalSpace());
getEGDrive(fileToUpload);
}
}
catch (FileNotFoundException e) {
Log.i("error create file uri", e.getLocalizedMessage());
e.printStackTrace();
} catch (IOException e) {
Log.i("error create file uri", e.getLocalizedMessage());
e.printStackTrace();
}
And here's the function to transform the InputStream into byteArray:
public byte[] getBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, len);
}
return byteBuffer.toByteArray();
}
Got big part of the answer from: https://stackoverflow.com/a/10297073/14990708

Reading a File line by line

I'm working on the creation of an Android App to track financial expenses and I would like to allow the user to batch import financial transactions into my app. The transactions would be come as .csv/.txt files.
However, I'm getting a cryptic exception:
java.lang.AbstractMethodError: abstract method "android.content.IContentProvider android.content.ContentResolver.acquireUnstableProvider(android.content.Context, java.lang.String)"
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1780)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1394)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1247)
at android.content.ContentResolver.openInputStream(ContentResolver.java:967)
....
Workflow is as follow: User selects text file to import, App imports content.
Start the file selector:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*");
//intent.addCategory("CATEGORY_OPENABLE");
startActivityForResult(intent, REEQUEST_CODE_IMPORT);
Catch the result:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == REEQUEST_CODE_IMPORT) {
// Make sure the request was successful
Uri path = data.getData();
if (resultCode == RESULT_OK && path != null) {
InputStream inputStream = null;
try {
ContentResolver contentResolver = new ContentResolver(getContext()) {};
// Error happens in the next line
inputStream = contentResolver.openInputStream(path);
// Get the object of DataInputStream
DataInputStream in = new DataInputStream(inputStream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line = "";
while((line = br.readLine()) != null) {
// Do something meaningful...
}
} catch () {
// Catch the exceptions ( I have removed some boiler plate code here...)
} finally {
// Close the path ( I have removed some boiler plate code here...)
inputStream.close();
}
}
}
}
Android documentation helped me solve the issue: Access documents and other files from shared storage - Input Stream
Following code adaptation is working:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == REEQUEST_CODE_IMPORT) {
// Make sure the request was successful
Uri path = data.getData();
if (resultCode == RESULT_OK && path != null) {
StringBuilder stringBuilder = new StringBuilder();
try (InputStream inputStream = getActivity().getContentResolver().openInputStream(path);
BufferedReader reader = new BufferedReader(
new InputStreamReader(Objects.requireNonNull(inputStream)))) {
String line;
while ((line = reader.readLine()) != null) {
Log.d(TAG, line);
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
String content = stringBuilder.toString();
// Do something with it
}
}
}

Using SAF File Picker in Android 10 (Q) to copy files from Downloads to local app folder

Since Android 10 (API 29) I need to use the Storage Access Framework's File Picker to select GPX (GPS) files to copy from the Downloads folder to my local app folder. I have implemented the file picker and am able to select the GPX file, however the result data URI appears different to the filename (but unique) and I cannot seem to use it to copy the files. The rest of the code is the same "copy" code I used in previous versions of Android. What am I doing wrong and how should I best use the SAF File Picker to copy files? I haven't been able to find a recent (API 29) "file copy" example on the net...
private static final int READ_REQUEST_CODE = 42;
...
public void performFileSearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// intent.setType("application/gpx"); // Filters GPX file but wont let me select them.
intent.setType("*/*");
startActivityForResult(intent, READ_REQUEST_CODE);
}
...
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (data != null) {
uri = data.getData();
handleDownloadedGPXFiles2(uri);
}
}
...
private void handleDownloadedGPXFiles2(Uri selectedFileUri) {
File sourceFile = new File(selectedFileUri.getPath()); // Returns a unique number or string but NOT filename string???
File destDirectory = new File(this.getExternalFilesDir(null), "Imported");
File destFile = new File(destDirectory, "test.gpx"); // Needs to be same name as original filename.
try {
if (!destFile.exists()) {
destFile.createNewFile();
}
FileInputStream inStream = new FileInputStream(sourceFile);
FileOutputStream outStream = new FileOutputStream(destFile);
FileChannel inChannel = inStream.getChannel();
FileChannel outChannel = outStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inStream.close();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(getApplicationContext(), "File Import Complete", Toast.LENGTH_LONG).show();
}
File sourceFile = new File(selectedFileUri.getPath());
Remove above line.
FileInputStream inStream = new FileInputStream(sourceFile);
Replace that line by:
InputStream inStream = getContentResolver().openInputStream(selectedFileUri);
Further you can remove
if (!destFile.exists()) {
destFile.createNewFile();
}
as the file will be created by the new FileOutputStream();
Finally: Your last Toast() is on the wrong place. It should be in the try block.
Place a different Toast() in the catch block to inform yourself or the user.
Thanks blackapps. Final code works well...
private void handleDownloadedGPXFiles2(Uri selectedFileUri) {
String displayName = "imported.gpx";
String fileExtension;
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(selectedFileUri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
if (displayName != null && displayName.length() >=4) {
fileExtension = displayName.substring(displayName.length() - 4);
if (!fileExtension.equals(".gpx")){
myCustomToast("Must be a .GPX file!");
return;
}
} else {
myCustomToast("Must be a .GPX file!");
return;
}
}
File destDirectory = new File(this.getExternalFilesDir(null), "Imported");
File destFile = new File(destDirectory, displayName);
FileOutputStream outStream = new FileOutputStream(destFile);
InputStream in = getContentResolver().openInputStream(selectedFileUri);
OutputStream out = outStream;
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.flush();
out.close();
Toast.makeText(getApplicationContext(), "File Import Complete", Toast.LENGTH_LONG).show();
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "File Import FAILED", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
finally
{
if (cursor != null)
cursor.close();
}
}

Access a text file in android

I'm trying to read the contents of a flat file stored in the phone's memory. But when I get the file address through the attempt, and I create the File object this is not readable.
Try to create it by manually passing the file's address and it works.
The problem is that I can not get that address from the uri that gives me the Intent
Here I open the android file browser
btnAdjuntar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("text/plain");
Intent chooser = Intent.createChooser(intent, "Elige App");
if (chooser.resolveActivity(getPackageManager()) != null) {
startActivityForResult(chooser, COD_ABRIR_FICHERO);
}
}
});
Here I receive the Intent
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == COD_ABRIR_FICHERO) {
if (resultCode == RESULT_OK) {
String ruta = data.getData().getPath();
String lectura;
try {
lectura = leerArchivo(ruta);
} catch (Exception e) {
lectura = e.getMessage();
}
Toast.makeText(getApplicationContext(), lectura, Toast.LENGTH_LONG).show();
}
}
}
The exception is thrown and you have this message:
/document/primary:Documents/PruebaArchivo.txt: open failed: ENOENT (No such file or directory)
Method leerArchivo
public String leerArchivo(String ruta) throws FileNotFoundException, IOException {
String cadena;
String lectura = "";
File file = new File(ruta);
FileReader f = new FileReader(file);
BufferedReader b = new BufferedReader(f);
while ((cadena = b.readLine()) != null) {
lectura += cadena;
}
b.close();
return lectura;
}
Already the permissions to read the external memory are accepted
You can open an InputStream from a Uri:
InputStream is = getContentResolver().openInputStream(uri);
Then wrap the input stream to create a BufferedReader:
BufferedReader b = new BufferedReader(new InputStreamReader(is));
while ((cadena = b.readLine()) != null) {
lectura += cadena;
}
b.close();
etc.
This way you never need to get the file's address. You can specify the encoding (e.g. "UTF-8") as a second parameter to the InputStreamReader constructor.
But when I get the file address through the attempt, and I create the File object this is not readable.
You are not getting a file. You are getting content. You are being given a Uri. A Uri is not a file.
String ruta = data.getData().getPath();
That line is only useful if the scheme of the Uri is file. In your case, the scheme is probably content.
Use a ContentResolver and openInputStream() to get an InputStream on the content. This works whether the Uri has a file scheme or a content scheme.

How to get Base64 String from InputStream?

I'm on a problem by taking the selected gallery picture and want to save it first as Base64 String in a XML file (for later use. For example if you exit the app and open it again).
As you can see I get the Image on a InputStream
But first of all the onClick method:
public void onClick(DialogInterface dialog, int which) {
pictureActionIntent = new Intent(Intent.ACTION_GET_CONTENT);
pictureActionIntent.setType("image/*");
startActivityForResult(pictureActionIntent,GALLERY_PICTURE);
}
Now in the onActivityResult method I want to store the image from InputStream to Base64 String.
case GALLERY_PICTURE:
if (resultCode == RESULT_OK && null != data) {
InputStream inputstream = null;
try {
inputstream = getApplicationContext().getContentResolver().openInputStream(data.getData());
Base64InputStream in = new Base64InputStream(inputstream,0);
} catch (IOException e) {
e.printStackTrace();
}
#EDIT
This is what I do after creating the base64 String.
Bitmap bmp = base64EncodeDecode.decodeBase64(Items.get("image"));
Image1.setImageBitmap(bmp);
And this is the decoding Method:
public Bitmap decodeBase64(String input) {
byte[] decodedByte = Base64.decode(input, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length);
}
I tried to use Base64InputStream but without success.
Can you give me a hint how to get from InputStream to Base64 String?
How many steps it will take doesn't matter.
I hope someone can help me!
Kind Regards!
Write these lines in onActivityResult method
try {
// get uri from Intent
Uri uri = data.getData();
// get bitmap from uri
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
// store bitmap to file
File filename = new File(Environment.getExternalStorageDirectory(), "imageName.jpg");
FileOutputStream out = new FileOutputStream(filename);
bitmap.compress(Bitmap.CompressFormat.JPEG, 60, out);
out.flush();
out.close();
// get base64 string from file
String base64 = getStringImage(filename);
// use base64 for your next step.
} catch (IOException e) {
e.printStackTrace();
}
private String getStringImage(File file){
try {
FileInputStream fin = new FileInputStream(file);
byte[] imageBytes = new byte[(int)file.length()];
fin.read(imageBytes, 0, imageBytes.length);
fin.close();
return Base64.encodeToString(imageBytes, Base64.DEFAULT);
} catch (Exception ex) {
Log.e(tag, Log.getStackTraceString(ex));
toast("Image Size is Too High to upload.");
}
return null;
}
you can use base64 String of image.
Also don't forget to add permissions in AndroidManifest.xml file READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE
EDIT:: Decode base64 to bitmap
byte[] bytes = Base64.decode(base64.getBytes(), Base64.DEFAULT);
ImageView image = (ImageView) this.findViewById(R.id.ImageView);
image.setImageBitmap(
BitmapFactory.decodeByteArray(bytes, 0, bytes.length)
);
Hope it'll work.
This should work:
public static byte[] getBytes(Bitmap bitmap) {
try{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
stream.flush();
//bitmap.compress(CompressFormat.PNG, 98, stream);
bitmap.compress(CompressFormat.JPEG, 98, stream);
//bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
return stream.toByteArray();
} catch (Exception e){
return new byte[0];
}
}
public static String getString(Bitmap bitmap){
byte [] ba = getBytes(bitmap);
String ba1= android.util.Base64.encodeToString(ba, android.util.Base64.DEFAULT);
return ba1;
}
Got this code from something I use in an application, stripped it down to the most basic as far as i know.
If you are selecting image from Gallery then why you are saving it as Base64 string in xml file , you can reuse that image from gallery .
For this save image url in SharedPreferences and use that url again to show image .
Edit :
If you want to store it locally then you can use SQLite Database to store it , for more detail visit this link .

Categories

Resources