【问题标题】:how to open inputstream for a "virtual" file on the cloud?如何为云上的“虚拟”文件打开输入流?
【发布时间】:2017-09-26 09:31:25
【问题描述】:

尝试转换我从Intent.ACTION_GET_CONTENT 获得的URI,输入流对本地文件打开正常,但对于来自驱动器 (/document/acc=1;doc=4089) 的URI,我得到一个FileNotFoundException,说文件是“虚拟的”。如何打开此类文件的输入流?

获取 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!"); 
    }

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());
                }

获取输入流:

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

结果:

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

获取字符串 - content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3D4089

URI 方案内容

URI 授权:com.google.android.apps.docs.storage

URI 片段:null

URI 路径段:

文档

acc=1;doc=4089

解析的类型(内容):application/vnd.google-apps.spreadsheet

分机:空

例外:

java.io.FileNotFoundException:文件是虚拟的:acc=1;doc=4089

【问题讨论】:

  • 当然可以。请出示您的代码。
  • but for URI from drive (/document/acc=1;doc=4089) I 那不是 uri。没有内容方案。请说出完整的方案。
  • @greenapps 这是我的代码和结果
  • 你也应该打印importedFileUri.toString()。因为那是真正的内容方案。
  • Trying to convert a URI。我没有看到您正在尝试更改 uri。这当然不需要。

标签: android file cloud uri android-contentprovider


【解决方案1】:

检查 URI 中的文件是否为虚拟文件:

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;
   }
}

从虚拟文件中获取输入流:

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();
    }

总体

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

更多信息here

【讨论】:

    【解决方案2】:

    由于应用无法使用 openInputStream() 方法直接打开虚拟文件,因此如果您在 ACTION_OPEN_DOCUMENT 意图中包含 CATEGORY_OPENABLE 类别,您的应用不会收到任何虚拟文件。

    要打开虚拟文件,您的客户端应用需要包含特殊逻辑来处理它们。如果您想获取文件的字节表示形式(例如预览文件),您需要向文档提供者请求备用 MIME 类型。

    要在您的应用中获取虚拟文档的 URI,首先您创建一个 Intent 以打开文件选择器 UI,就像之前在搜索文档中显示的代码一样。

    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;
    }
    

    验证文件是虚拟文件后,您可以将文件强制转换为其他 MIME 类型,例如图像文件。下面的代码 sn -p 显示了如何检查虚拟文件是否可以表示为图像,如果可以,则从虚拟文件中获取输入流。

    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();
    }
    

    描述和代码 sn-p 取自 android 文档。查看链接和参考以了解有关存储访问框架的更多信息。

    Youtube

    Reference

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-22
      • 2022-01-11
      • 2016-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多