Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to open inputstream for a "virtual" file on the cloud?

Trying to convert a URI that I got from Intent.ACTION_GET_CONTENT, input stream opens fine for local files, but for URI from drive (/document/acc=1;doc=4089) I get a FileNotFoundException, saying the file is "virtual". How can I open an input stream for such files?

Getting the URI:

 Intent i = new Intent(Intent.ACTION_GET_CONTENT);
 i.setType("*/*"); //No I18N
 try{
    startActivityForResult(Intent.createChooser(i, "Pick a file"), REQUEST_CODE_UPLOAD_FILE_FOR_IMPORT);
    }catch (android.content.ActivityNotFoundException ex){
    Log.e("Error","FileManager not found!"); 
    }

and

importedFileUri = data.getData();
                System.out.println("URI path: "+importedFileUri.getPath()+" "+importedFileUri.getEncodedPath());
                System.out.println("URI Scheme "+importedFileUri.getScheme());
                System.out.println("URI Authority :"+importedFileUri.getAuthority());
                System.out.println("URI Fragment :"+importedFileUri.getFragment());
                System.out.println("URI path segments : ");
                for(String str : importedFileUri.getPathSegments()){
                    System.out.println("\t" +str );
                }
                String ext;
                if (importedFileUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
                    final MimeTypeMap mime = MimeTypeMap.getSingleton();
                    ext = mime.getExtensionFromMimeType(mActivity.getContentResolver().getType(importedFileUri));
                    System.out.println("resolved type (content) : "+ mActivity.getContentResolver().getType(importedFileUri));
                } else {
                    ext = MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(new File(importedFileUri.getPath())).toString());
                    System.out.println("resolved type (other) : "+ Uri.fromFile(new File(importedFileUri.getPath())).toString());
                }

Getting the inputstream:

InputStream is = null;
                try {
                    is = contentResolver.openInputStream(importedFileUri);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }

Result:

URI path: /document/acc=1;doc=4089 /document/acc%3D1%3Bdoc%3D4089

GetString - content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3D4089

URI Scheme content

URI Authority :com.google.android.apps.docs.storage

URI Fragment :null

URI path segments :

document

acc=1;doc=4089

resolved type (content) : application/vnd.google-apps.spreadsheet

ext : null

The Exception:

java.io.FileNotFoundException: File is virtual: acc=1;doc=4089

like image 894
ColonD Avatar asked Sep 26 '17 09:09

ColonD


2 Answers

Check if File at URI is Virtual or not:

private static boolean isVirtualFile(Uri uri) {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
       if (!DocumentsContract.isDocumentUri(activity, uri)) {
           return false;
       }

       Cursor cursor = activity.getContentResolver().query(
            uri,
            new String[]{DocumentsContract.Document.COLUMN_FLAGS},
            null, null, null);
       int flags = 0;
       if (cursor.moveToFirst()) {
           flags = cursor.getInt(0);
       }
       cursor.close();
       return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
   } else {
       return false;
   }
}

Get Input stream from Virtual file with this:

private static InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
            throws IOException {

        ContentResolver resolver = activity.getContentResolver();

        String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);

        if (openableMimeTypes == null ||
                openableMimeTypes.length < 1) {
            throw new FileNotFoundException();
        }

        return resolver
                .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
                .createInputStream();
    }

Overall

if (isVirtualFile(sourceuri)) {
   input = getInputStreamForVirtualFile(sourceuri, getMimeType(name));
} else {
   input = activity.getContentResolver().openInputStream(sourceuri);
}

More information here.

like image 126
Ankur Avatar answered Nov 15 '22 07:11

Ankur


Since an app cannot directly open a virtual file by using the openInputStream() method, your app does not receive any virtual files if you include the CATEGORY_OPENABLE category in the ACTION_OPEN_DOCUMENT intent.

To open virtual files, your client app needs to include special logic to handle them. If you want to get a byte representation of the file—to preview the file, for example—you need to request for an alternate MIME type from the documents provider.

To get a URI for a virtual document in your app, first you create an Intent to open the file picker UI, like the code shown previously in Seach for documents.

private boolean isVirtualFile(Uri uri) {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false;
    }

    Cursor cursor = getContentResolver().query(
        uri,
        new String[] { DocumentsContract.Document.COLUMN_FLAGS },
        null, null, null);

    int flags = 0;
    if (cursor.moveToFirst()) {
        flags = cursor.getInt(0);
    }
    cursor.close();

    return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}

After you verify that the file is virtual, you can then coerce the file into an alternative MIME type such as an image file. The following code snippet shows how to check whether a virtual file can be represented as an image, and if so, gets an input stream from the virtual file.

private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
    throws IOException {

    ContentResolver resolver = getContentResolver();

    String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);

    if (openableMimeTypes == null ||
        openableMimeTypes.length &lt; 1) {
        throw new FileNotFoundException();
    }

    return resolver
        .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
        .createInputStream();
}

The description and code snippet are taken from the android docs. Check out the link and reference to learn more about Storage Access Framework.

Youtube

Reference

like image 36
svg Avatar answered Nov 15 '22 06:11

svg