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.
Related
I have been working for a long time on my android app project with Amplify Storage, and I have faced a problem which I didn’t find a solution for.
I want to retrieve an image/video from the gallery and upload it to S3, but I always get an error with “Cursor” it always returns null.
Is there any better way to convert Uri data to a File so I can upload it to S3?
here is my code:
public void openPhotoGallery(View v) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select file to upload "), 8);
}
#Override
protected void onActivityResult(int requestCode, final int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 8) {
Uri selectedMediaUri = data.getData();
String filePath = getPath(selectedMediaUri);
File file = saveVideoToInternalStorage(filePath);
Amplify.Storage.uploadFile("test/image", file, result -> {
Log.i("MyAmplifyApp", "Successfully uploaded: " + result.getKey());
file.delete();
}, error -> {
Log.e("MyAmplifyApp", "Upload failed", error);
});
}
}
public String getPath(Uri uri) {
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(uri, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); // Line of error
cursor.moveToFirst();
return cursor.getString(column_index);
}
private File saveVideoToInternalStorage (String filePath) {
File newfile = null;
try {
File currentFile = new File(filePath);
ContextWrapper cw = new ContextWrapper(getApplicationContext());
newfile = new File(this.getFilesDir().getPath().toString() + "/video1.mp4");
if(currentFile.exists()){
InputStream in = new FileInputStream(currentFile);
OutputStream out = new FileOutputStream(newfile);
// Copy the bits from instream to outstream
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
Log.v("", "Video file saved successfully.");
} else {
Log.v("", "Video saving failed. Source file missing.");
}
} catch (Exception e) {
e.printStackTrace();
}
return newfile;
}
And I always have this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.abdulelah.taajerpartners, PID: 24163
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=8, result=-1, data=Intent { dat=content://media/external/images/media/419319 flg=0x1 (has extras) }} to activity {com.abdulelah.taajerpartners/com.ajjerly.partners.TestActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'int android.database.Cursor.getColumnIndexOrThrow(java.lang.String)' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:5078)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:5120)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:49)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2199)
at android.os.Handler.dispatchMessage(Handler.java:112)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7625)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int android.database.Cursor.getColumnIndexOrThrow(java.lang.String)' on a null object reference
at com.ajjerly.partners.TestActivity.getPath(TestActivity.java:114)
at com.ajjerly.partners.TestActivity.onActivityResult(TestActivity.java:72)
at android.app.Activity.dispatchActivityResult(Activity.java:7797)
at android.app.ActivityThread.deliverResults(ActivityThread.java:5071)
As of Android 10, you can only obtain the path for a given Uri if the File is stored within your application directory. Photos from the Gallery do not meet this criteria. See Storage updates in Android for more details on this.
One solution would be to create an InputStream directly from your Uri, like this:
InputStream inStream = getContentResolver().openInputStream(uri);
Then, you can save that to a File, like you are doing in saveVideoToInternalStorage, and then pass the File to Amplify.Storage.uploadFile.
An even better/easier solution would be to use the Amplify.Storage.uploadInputStream API (instead of Amplify.Storage.uploadFile), like this:
InputStream inStream = getContentResolver().openInputStream(uri);
Amplify.Storage.uploadInputStream("test/image", inputStream,
result -> {
Log.i("MyAmplifyApp", "Successfully uploaded: " + result.getKey());
}, error -> {
Log.e("MyAmplifyApp", "Upload failed", error);
}
);
Under the hood, the Amplify library actually does the same thing you are doing - it writes the InputStream to a temp directory as a File, uploads that File, and then deletes it from the temp directory on completion.
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
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
}
}
}
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();
}
}
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.