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

2

8

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

Jerad answered 26/9, 2017 at 9:31 Comment(21)
Yes of course. Please show your code.Regain
but for URI from drive (/document/acc=1;doc=4089) I That is no uri. No content scheme. Tell the complete scheme please.Regain
@Regain this is my code and my resultJerad
You should als print importedFileUri.toString(). As that is the real content scheme.Regain
Trying to convert a URI. I do not see that you are trying to change the uri. That is not needed of course.Regain
Sorry the wording is wrong, I meant creating an InputStream from the URI so that I can get a Byte Array of the file which I can the upload to another serverJerad
ok, so i got URI getString :content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3D4089 . How can i get an inputstream fro this url/uri?Jerad
URI getString ? toString() you mean? Your code is ok.Regain
I just tried ACTION_GET_CONTENT and picked something from google drive and the content scheme looks like yours and the stream is opened ok.Regain
I'm getting java.io.FileNotFoundException: File is virtual: acc=1;doc=4089 when i do is = contentResolver.openInputStream(importedFileUri);. should i replace it with importedFileUri.toString or something?Jerad
No. I said your code was ok. You are not messing around with the uri? Something you did not post?Regain
Android version? Or special device? Tried other devices?Regain
nope, I just got URI from data.getData() and made no change.Jerad
Its a Samsung Galaxy S8 with Nougat(7.0) , and i dont have any other device at hand. lemme see if I can get oneJerad
The error only occurs for drive files. Local files are okay.Jerad
I tried it out on a Moto G(6.0.3) and a BAD REQUEST exception is thrown afterward in my HTTP request(where I upload this file to server). I assume this is the same error (bad request implying there is something wrong with what i sent). so the error seems to be device specific, although in both cases I still cant use the resultant byte arrayJerad
You should not mess around with upload and http. You just should try to open an input stream in onActivityResult() directly.Regain
no, no... the upload is all later here is the flow intent -> uri -> inputstream -> bytearray -> contentbody -> http. the http request takes place after i get the byte array from the inputstreamJerad
here too, the code only fails for non-local filesJerad
Let us continue this discussion in chat.Jerad
You should open an input stream directly in onActivityResult(). Please report.Regain
O
8

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.

Ordain answered 27/11, 2017 at 23:11 Comment(0)
W
2

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

Walther answered 21/12, 2018 at 19:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.