I have an app which allows to select photos with an external app. Then I take the path of the photo from the uri and use it for internal actions.
When user selects a photo with Google Photo, if the picture is locally stored then the next code works perfectly. But if the picture is in the cloud the result of cursor.getString(index) is null.
I've search for some info, but not sure about the solution
final String[] projection = { "_data" };
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow("_data");
return cursor.getString(index);
}
Thank you!
Finally and according to #CommonsWare answer and the previous post about this issue I solved getting the InputStream from the uri, coping into a new temporal file and passing the path to the function I need to use.
Here is the simplified code:
public String getImagePathFromInputStreamUri(Uri uri) {
InputStream inputStream = null;
String filePath = null;
if (uri.getAuthority() != null) {
try {
inputStream = getContentResolver().openInputStream(uri); // context needed
File photoFile = createTemporalFileFrom(inputStream);
filePath = photoFile.getPath();
} catch (FileNotFoundException e) {
// log
} catch (IOException e) {
// log
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return filePath;
}
private File createTemporalFileFrom(InputStream inputStream) throws IOException {
File targetFile = null;
if (inputStream != null) {
int read;
byte[] buffer = new byte[8 * 1024];
targetFile = createTemporalFile();
OutputStream outputStream = new FileOutputStream(targetFile);
while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
outputStream.flush();
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return targetFile;
}
private File createTemporalFile() {
return new File(getExternalCacheDir(), "tempFile.jpg"); // context needed
}
When user selects a photo with Google Photo, if the picture is locally stored then the next code works perfectly.
Not necessarily. There is no requirement for that Uri to respond with a _data column to a query(). There is no requirement for the value it returns to be useful to you (e.g., a file on internal storage or removable storage that you cannot access).
If you need the photo loaded into an ImageView, pass the Uri to an image-loading library, such as Picasso.
If you need the bytes of the photo, use openInputStream() with ContentResolver to get an InputStream on the content identified by the Uri. Please open and read from the InputStream on a background thread.
Related
I am trying to write an image on storage then reading it. Write operation is successful but read fails. I have tried using intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); when calling the responsible activity but was of no help.
This is how I write image:
public void savepic(Bitmap pic)
{
SQLiteDatabase db = dBcontract.getWritableDatabase();
ContentValues values = new ContentValues();
FileOutputStream out = null;
try
{
File image = createImageFile();
Uri photoURI = FileProvider.getUriForFile(context, "lcukerd.com.android.fileprovider", image);
out = new FileOutputStream(image);
pic.compress(Bitmap.CompressFormat.PNG, 100, out);
values.put(eventDBcontract.ListofItem.columnpic, photoURI.toString());
db.insert(eventDBcontract.ListofItem.tableName2, null, values);
Log.i(TAG, "Pic saved " + photoURI.toString());
} catch (Exception e)
{
e.printStackTrace();
} finally
{
try
{
if (out != null)
{
out.close();
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}
private File createImageFile() throws IOException
{
String EName = "Image";
File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(EName, ".jpg", storageDir);
return image;
}
This is how I read image:
public Bitmap getDownloadedpics(int index)
{
SQLiteDatabase db = dBcontract.getReadableDatabase();
Cursor cursor = db.query(eventDBcontract.ListofItem.tableName2, projection2, null, null, null, null, null);
cursor.moveToPosition(index);
Bitmap photo = null;
try
{
Log.d(TAG,cursor.getString(cursor.getColumnIndex(eventDBcontract.ListofItem.columnpic)));
photo = MediaStore.Images.Media.getBitmap(context.getContentResolver(),
Uri.parse(cursor.getString(cursor.getColumnIndex(eventDBcontract.ListofItem.columnpic))));
} catch (IOException e)
{
Log.e(TAG, "Can't read image");
}
Log.d(TAG, "Returned " + String.valueOf(cursor.getCount()) + " pics");
return (photo);
}
I get Exception at `photo =
MediaStore.Images.Media.getBitmap(context.getContentResolver(),
Uri.parse(cursor.getString(cursor.getColumnIndex(eventDBcontract.ListofItem.columnpic))));
Exception is :
java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{6118b35 21772:lcukerd.com.instaswipe/u0a157} (pid=21772, uid=10157) that is not exported from uid 10101
I have checked other similar question but seems like they are all for some other type of problem. Help me solve it.
Instead of photoUri.toString() put image.getAbsolutePath() in your database.
If you want to read the file get the path from the database. Construct a File object en use FileInputStream. Then let BitmapFactory read the data from the stream.
My app takes photo from camera and saves it in a file whose Uri is stored in SQL database. Initializing bitmap using Uri from database works flawlessly. However, when i try to initialize a file using Uri from database and then delete using imagefile.delete()it does not work. I have tried few methods given in other posts but none worked.
This i how i save file.
Intent for Camera:
Intent startCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (startCamera.resolveActivity(getPackageManager())!=null)
{
photoFile= null;
try{
photoFile = createImageFile();
}
catch (IOException ex)
{
Log.e("File Creation","error");
}
if (photoFile!=null)
{
photoURI = FileProvider.getUriForFile(context,"lcukerd.com.android.fileprovider",photoFile);
startCamera.putExtra(MediaStore.EXTRA_OUTPUT,photoURI);
camerastarted=true;
startActivityForResult(startCamera,CAMERA_REQUEST);
}
}
Declared method:
private File createImageFile() throws IOException
{
String EName = "Stuff";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(EName,".jpg",storageDir);
return image;
}
Compressing image:
photoFile.delete();
FileOutputStream out = null;
try {
File image = createImageFile();
photoURI = FileProvider.getUriForFile(context, "lcukerd.com.android.fileprovider", image);
out = new FileOutputStream(image);
photo.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
saving Uri to database is simple conversion of Uri to string and then saving in TEXT column of table.
Help me delete image file .
Edit:
I tried following.
private void deleteimage(String imageloc)
{
File imagefile = new File(getRealPathFromURI(Uri.parse(imageloc)));
Log.d("file deletion",String.valueOf(imagefile.delete())+" "+imageloc);
}
public String getRealPathFromURI( Uri contentUri) {
String result;
Cursor cursor = getContentResolver().query(contentUri, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentUri.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
and..
private void deleteimage(String imageloc)
{
File imagefile = new File(Uri.parse(imageloc).getpath());
Log.d("file deletion",String.valueOf(imagefile.delete())+" "+imageloc);
}
None worked.
Since this is a Uri that you are getting from FileProvider, call delete() on a ContentResolver, passing in the Uri (plus null for the remaining parameters). That will delete the underlying file, at least according to the FileProvider documentation.
Or, store the file path, rather than the Uri, in your database, since this is your file. You can recreate the Uri later as needed using FileProvider.getUriForFile(), and you can use delete() on File to delete the file, given its path.
I want to know why this error comes when try to upload file with real device using Marshmallow. Can you please explain the solution for this.
12-28 10:39:32.606: E/3(17552): Excption : java.io.FileNotFoundException: /storage/emulated/0/WoodenstreetD.doc: open failed: ENOENT (No such file or directory)
I was stuck with this for last week and still getting same issue.
Thanks In Advance.
This error comes due to wrong path. The path your getting from onActivityResult is not correct. Use this to get path of selected file.
public static String getRealPathFromUri(Context ctx, Uri uri) {
String[] filePathColumn = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = ctx.getContentResolver().query(uri, filePathColumn,
null, null, null);
if (cursor != null) {
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
picturePath = cursor.getString(columnIndex);
Log.e("", "picturePath : " + picturePath);
cursor.close();
}
return picturePath;
}
picturePath gives you full path of image/file.
It'll work for me.
i worte this and it'll work for me.
public static java.io.File convertUriTFile(Activity activity, Uri uri) {
InputStream inputStream = null;
java.io.File file = null;
try {
inputStream = activity.getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
file = new java.io.File(activity.getCacheDir(), "temp");
try (OutputStream output = new FileOutputStream(file)) {
byte[] buffer = new byte[4 * 1024];
int read;
while ((read = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.flush();
} catch (IOException e) {
e.printStackTrace();
}
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
What I am trying to achieve. I have a button, when the button is clicked the app opens a file picker and the user selects a file. The app then uses a FileInputStream to read the file and generates a byte[]. I have a TextView below the button which will then simply display the byte[].length. Here is the code in the button.onClick() event:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
requestFilePickerCode = parent.registerActivityResultListener(this);
try
{
parent.startActivityForResult(intent, requestFilePickerCode);
}
catch (ActivityNotFoundException e)
{
Toast.makeText(task.getParent(), "Please install a file manager", Toast.LENGTH_SHORT).show();
}
Now this code works and I have confirmed that it fires onActivityResult when the file is chosen. I simply print a Log to display data.toString() which produces the following output:
11-02 15:14:36.196 2535-2535/? V/class za.co.gpsts.gpsjobcard.utility.handlers.PebbleTypeHandlerBinary: -----> content:/com.android.providers.downloads.documents/document/1
So it seems to be getting the selected file. When I run the app and I select a file it throws my custom error:
11-02 15:14:36.196 2535-2535/? E/class za.co.gpsts.gpsjobcard.utility.handlers.PebbleTypeHandlerBinary: -----> File does not exist
This obviously indicates that I am not getting the file. Here is my code:
#Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
{
byte[] fileContent;
// check that data is not null and assign to file if not null
if (data != null)
{
Uri uri = data.getData();
String uriString = uri.toString();
file = new File(uriString);
Log.v(PebbleTypeHandlerBinary.class.toString(), "-----> " + file.toString());
// declare file input stream and read bytes
// write to string variable to test and test output
FileInputStream fin = null;
try
{
fin = new FileInputStream(file);
fileContent = new byte[(int) file.length()];
fin.read(fileContent);
String test = new String(fileContent);
Log.v(PebbleTypeHandlerBinary.class.toString(), "=====> " + test);
}
catch (FileNotFoundException e)
{
Toast.makeText(task.getParent(), "File not found", Toast.LENGTH_SHORT).show();
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> File does not exist");
}
catch (IOException e)
{
Toast.makeText(task.getParent(), "Error reading file", Toast.LENGTH_SHORT).show();
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error while reading the file");
}
finally
{
// close the file input stream to stop mem leaks
try
{
if (fin != null)
{
fin.close();
}
} catch (IOException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error closing the stream");
}
}
Log.v(PebbleTypeHandlerBinary.class.toString(), data.toString());
}
return false;
}
Please can you guys review my code and help me to get this working. Any help would be appreciated.
/* you can get just name and size with this method.
use Cursor .
Uri uri = data.getData();
get data from onActivityResult()
*/;
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
cursor.moveToFirst();
String name = cursor.getString(nameIndex);
String size = Long.toString(cursor.getLong(sizeIndex));
Toast.makeText(this, "name : "+name+"\nsize : "+size, Toast.LENGTH_SHORT).show();
I managed to fix it as follows:
I used inputStream = task.getParent().getContentResolver().openInputStream(uri); to get an InputStream. Then used a ByteArrayOutputStream to write to a byte[]. See code below.
#Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
{
Uri uri = data.getData();
byte[] fileContent;
InputStream inputStream = null;
try
{
inputStream = task.getParent().getContentResolver().openInputStream(uri);
if (inputStream != null)
{
fileContent = new byte[(int)file.length()];
inputStream.read(fileContent);
fileContent = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read;
while((read=inputStream.read(fileContent))>-1) baos.write(fileContent,0,read);
fileContent = baos.toByteArray();
baos.close();
Log.v(PebbleTypeHandlerBinary.class.toString(), "-----> Input Stream: " + inputStream);
Log.v(PebbleTypeHandlerBinary.class.toString(), "-----> Byte Array: " + fileContent.length);
}
else
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Input Stream is null");
}
}
catch (FileNotFoundException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> File not found", e);
}
catch (IOException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error reading file", e);
}
finally
{
if (inputStream != null)
{
try
{
inputStream.close();
}
catch (IOException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error reading file", e);
}
}
}
return false;
}
Thanks for all your help.
U can search converting uri to filepath.
GetData() retruns a uri.
But new File() need a filepath param;
Like this:
public static String getRealFilePath( final Context context, final Uri uri ) {
if ( null == uri ) return null;
final String scheme = uri.getScheme();
String data = null;
if ( scheme == null )
data = uri.getPath();
else if ( ContentResolver.SCHEME_FILE.equals( scheme ) ) {
data = uri.getPath();
} else if
( ContentResolver.SCHEME_CONTENT.equals( scheme ) ) {
Cursor cursor = context.getContentResolver().query( uri, new String[] { ImageColumns.DATA }, null, null, null );
if ( null != cursor ) {
if ( cursor.moveToFirst() ) {
int index = cursor.getColumnIndex( ImageColumns.DATA );
if ( index > -1 ) {
data = cursor.getString( index );
}
}
cursor.close();
}
}
return data;
}
1. File Name:
You can get the file name with the following method:
public static String getFileName(Context context, Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
You will call it inside onActivityResult() and pass to it the context and uri. and you can find the uri through the intent you get from the onActivityResult which is mostly called "data", you will get the Uri from it like this:
data.data
which ".data" is the Uri.
Ex:
Utils.getFileName(this, data!!.data)
And finally it will return the file name as a String.
2. File Path:
Simply to get the file path you can get it from the data intent uri like this:
data!!.data!!.path.toString()
It will get you the path of the file as a String.
how to read from a zipfile in a folder which was selected by the ACTION_OPEN_DOCUMENT_TREE Intent ?
My app let the user choose a folder through the ACTION_OPEN_DOCUMENT_TREE Intent.
In that folder i will have a Zipfile with a specific name.
Goal is to read that Zipfile with java.util.zip.ZipFile.
How do I instantiate a ZipFile with this specific Zipfilename from the provided URI (Folderinfo) in onActivityResult ?
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Uri treeUri = data.getData();
String sPath=treeUri.getPath();
java.util.zip.ZipFile myzip=new java.util.zip.ZipFile("mypath"); <-- Constructor expects File or Path as String. Howto handle this with the Uri ?
How do I instantiate a ZipFile with this specific Zipfilename from the provided URI (Folderinfo) in onActivityResult ?
You can't, as there is no file, and ZipFile needs a file. You can only get an InputStream, using openInputStream() on a ContentResolver, and that only if you get a Uri to that specific file that you are looking for.
Your options appear to be:
Use ZipInputStream, which can wrap an InputStream
Find some third-party library that accepts an InputStream as input and gives you a better API
Copy the ZIP flie to your app's portion of internal storage and read its contents using ZipFile
I was working on this problem and ended up going with a strategy #CommonsWare mentioned as the third option, which is copying a file to non-sdcard place and load as a ZipFile. It works well so I share my snippet for everybody.
public static ZipFile loadCachedZipFromUri(Context context, Uri uri, String filename){
File file = new File(context.getCacheDir(), filename);
String fileName = context.getCacheDir().getAbsolutePath() + '/' + filename;
ZipFile zip = null;
if (file.exists()){
Log.d(TAG, "file exists");
try {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // N for Nougat
zip = new ZipFile(fileName, Charset.forName("ISO-8859-1"));
}else{
zip = new ZipFile(fileName);
}
} catch (IOException e) {
e.printStackTrace();
}
return zip;
}
DocumentFile dest = DocumentFile.fromFile(file);
InputStream in = null;
OutputStream out = null;
Log.d(TAG, "Copy started");
try {
in = context.getContentResolver().openInputStream(uri);
out = context.getContentResolver().openOutputStream(dest.getUri());
byte[] buf = new byte[2048];
int len = 0;
while((len = in.read(buf)) >= 0){
out.write(buf, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d(TAG, "Load copied file");
try {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // N for Nougat
zip = new ZipFile(fileName, Charset.forName("ISO-8859-1"));
}else{
zip = new ZipFile(fileName);
}
} catch (IOException e) {
e.printStackTrace();
}
return zip;
}