当前位置: 首页 > 知识库问答 >
问题:

从内部存储创建和共享文件

长孙章横
2023-03-14

我的目标是在内部存储上创建一个XML文件,然后通过share Intent发送它。

我能够使用以下代码创建XML文件

FileOutputStream outputStream = context.openFileOutput(fileName, Context.MODE_WORLD_READABLE);
PrintStream printStream = new PrintStream(outputStream);
String xml = this.writeXml(); // get XML here
printStream.println(xml);
printStream.close();

我一直在尝试检索输出文件的Uri以共享它。我首先尝试通过将文件转换为Uri来访问该文件

File outFile = context.getFileStreamPath(fileName);
return Uri.fromFile(outFile);

返回file:///data/data/com.my.package/files/myfile.xml但我似乎无法将此附加到电子邮件、上载等。

如果我手动检查文件长度,它是正确的,并显示有一个合理的文件大小。

接下来,我创建了一个内容提供者并尝试引用该文件,但它不是该文件的有效句柄。ContentProvider似乎从来没有被称为任何一点。

Uri uri = Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + fileName);
return uri;

此返回content://com.my.package.provider/myfile.xml但我检查了文件,它的长度为零。

如何正确访问文件?我需要使用内容提供者创建文件吗?如果是,如何?

使现代化

这是我用来共享的代码。如果我选择Gmail,它确实显示为附件,但当我发送时,它会出现错误无法显示附件并且到达的电子邮件没有附件。

public void onClick(View view) {
    Log.d(TAG, "onClick " + view.getId());

    switch (view.getId()) {
        case R.id.share_cancel:
            setResult(RESULT_CANCELED, getIntent());
            finish();
            break;

        case R.id.share_share:

            MyXml xml = new MyXml();
            Uri uri;
            try {
                uri = xml.writeXmlToFile(getApplicationContext(), "myfile.xml");
                //uri is  "file:///data/data/com.my.package/files/myfile.xml"
                Log.d(TAG, "Share URI: " + uri.toString() + " path: " + uri.getPath());

                File f = new File(uri.getPath());
                Log.d(TAG, "File length: " + f.length());
                // shows a valid file size

                Intent shareIntent = new Intent();
                shareIntent.setAction(Intent.ACTION_SEND);
                shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
                shareIntent.setType("text/plain");
                startActivity(Intent.createChooser(shareIntent, "Share"));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }

            break;
    }
}

我注意到有一个异常从createChooser(…)内部抛出,但我不明白为什么会被扔出去。

活动线程(572):活动com.Android内部的应用程序。ChooseActivity已泄漏IntentReceiver com.Android内部的应用程序。分辨率$1@4148d658最初在这里注册的。是否缺少对unregisterReceiver()的调用?

我研究了这个错误,找不到任何明显的东西。这两个链接都表明我需要注销接收器。

  • ChooseActivity已泄漏IntentReceiver

我有一个接收器设置,但它适用于在其他地方设置的警报管理器,不需要应用程序注册/取消注册。

openFile(…)的代码

如果需要,这里是我创建的内容提供者。

public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    String fileLocation = getContext().getCacheDir() + "/" + uri.getLastPathSegment();

    ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
    return pfd;
}

共有3个答案

龚远
2023-03-14

如果您需要允许其他应用程序查看您的应用程序的私有文件(用于共享或其他),您可以节省一些时间,只需使用v4 compat library的FileProvider

太叔英卫
2023-03-14

所以我认为Rob的答案是正确的,但我的做法有点不同。据我所知,在提供程序中设置:

android:exported="true"

您正在授予对所有文件的公共访问权限?!无论如何,只授予对某些文件的访问权限的一种方法是通过以下方式定义文件路径权限:

<provider
    android:authorities="com.your.app.package"
    android:name="android.support.v4.content.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

然后在XML目录中定义file_paths.xml文件如下:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path path="/" name="allfiles" />
    <files-path path="tmp/" name="tmp" />
</paths>

现在,“allfiles”提供了与选项android:exported=“true”相同的公共权限,但我想你并不想这样,所以下一行就是定义一个子目录。然后,您所要做的就是将要共享的文件存储在该目录中。

接下来你要做的是,正如Rob所说,获取这个文件的URI。我是这样做的:

Uri contentUri = FileProvider.getUriForFile(context, "com.your.app.package", sharedFile);

然后,当我有这个URI时,我必须为其他应用程序附加使用它的权限。我正在使用此文件URI或将其发送到相机应用程序。无论如何,这就是我获取其他应用程序包信息并授予URI权限的方式:

PackageManager packageManager = getPackageManager();
List<ResolveInfo> list = packageManager.queryIntentActivities(cameraIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() < 1) {
    return;
}
String packageName = list.get(0).activityInfo.packageName;
grantUriPermission(packageName, sharedFileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

ClipData clipData = ClipData.newRawUri("CAMFILE", sharedFileUri);
cameraIntent.setClipData(clipData);
cameraIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
cameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(cameraIntent, GET_FROM_CAMERA);

我留下了camera的代码,因为我不想再拿其他一些我没有研究过的例子。但通过这种方式,您可以将权限附加到URI本身。

摄像头的功能是,我可以通过ClipData进行设置,然后额外设置权限。我猜在您的情况下,在将文件附加到电子邮件时,只需要FLAG\u GRANT\u READ\u URI\u权限。

这里是帮助FileProvider的链接,因为我的所有帖子都是基于我在那里找到的信息。但在查找摄像头应用程序的软件包信息时遇到了一些问题。

希望有帮助。

赫连睿
2023-03-14

可以通过ContentProvider公开存储在应用程序私有目录中的文件。下面是我制作的一些示例代码,展示了如何创建一个可以做到这一点的内容提供者。

显示

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.providertest"
  android:versionCode="1"
  android:versionName="1.0">

  <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="15" />

  <application android:label="@string/app_name"
    android:icon="@drawable/ic_launcher"
    android:theme="@style/AppTheme">

    <activity
        android:name=".MainActivity"
        android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <provider
        android:name="MyProvider"
        android:authorities="com.example.prov"
        android:exported="true"
        />        
  </application>
</manifest>

在ContentProvider中,重写openFile以返回ParcelFileDescriptor

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {       
     File cacheDir = getContext().getCacheDir();
     File privateFile = new File(cacheDir, "file.xml");

     return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
}

确保已将xml文件复制到缓存目录

    private void copyFileToInternal() {
    try {
        InputStream is = getAssets().open("file.xml");

        File cacheDir = getCacheDir();
        File outFile = new File(cacheDir, "file.xml");

        OutputStream os = new FileOutputStream(outFile.getAbsolutePath());

        byte[] buff = new byte[1024];
        int len;
        while ((len = is.read(buff)) > 0) {
            os.write(buff, 0, len);
        }
        os.flush();
        os.close();
        is.close();

    } catch (IOException e) {
        e.printStackTrace(); // TODO: should close streams properly here
    }
}

现在,任何其他应用程序都应该能够通过使用内容uri(content://com.example.prov/myfile.xml)为您的私人文件获取InputStream

对于一个简单的测试,从类似于以下的单独应用程序调用内容提供商

    private class MyTask extends AsyncTask<String, Integer, String> {

    @Override
    protected String doInBackground(String... params) {

        Uri uri = Uri.parse("content://com.example.prov/myfile.xml");
        InputStream is = null;          
        StringBuilder result = new StringBuilder();
        try {
            is = getApplicationContext().getContentResolver().openInputStream(uri);
            BufferedReader r = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = r.readLine()) != null) {
                result.append(line);
            }               
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try { if (is != null) is.close(); } catch (IOException e) { }
        }

        return result.toString();
    }

    @Override
    protected void onPostExecute(String result) {
        Toast.makeText(CallerActivity.this, result, Toast.LENGTH_LONG).show();
        super.onPostExecute(result);
    }
}
 类似资料:
  • 在我的应用程序中,视频是从外部存储器播放的,我保留了播放和共享的选项。通过点击共享按钮,用户可以共享视频,但存在一个问题,即视频无法共享。 这是我的文件路径。它是存在的。视频正在播放和删除,但未共享?? 这是为了共享文件(视频) 我如何分享我的视频?我应该实现什么来分享存储中的视频。你的回答对我来说很有价值

  • 共享内存是两个或多个进程共享的内存。 但是,为什么我们需要共享内存或其他通信方式呢? 重申一下,每个进程都有自己的地址空间,如果任何进程想要将自己的地址空间的某些信息与其他进程进行通信,那么只能通过IPC(进程间通信)技术进行。 我们已经知道,通信可以在相关或不相关的进程之间进行。 通常,使用管道或命名管道来执行相互关联的进程通信。 可以使用命名管道或通过共享内存和消息队列的常用IPC技术执行无关

  • 我试图使用Adobe reader读取从服务器下载的pdf文件,但问题是当我将其存储在内部存储时,其他应用程序无法读取该文件。现在我想知道如何将此文件复制到外部存储(/sdcard/)中,以便pdf查看器可以查看。 由于安全原因,我将文件存储在内部存储,然后删除外部存储的文件。 我的问题是如何复制保存在内部存储中的文件,而不使用raw或将其放入InputStream中的资产。

  • 我正在制作一个soundboard应用程序,当长按按钮1时,我需要共享sound1。我可以用以下代码创建共享菜单: 我可以与whatsapp和Google Drive完美共享音频文件,但其他应用程序不起作用。我听说您必须将文件复制到外部存储,并从那里共享它们。我已经搜索了将近两天,但我找不到这样做的方法。Stack上的其他文章也帮不了我:/ 如何在外部存储器中创建目录,将文件(sound1.ogg

  • DPDK Ring DPDK Ring提供了一个FIFO无锁队列,支持丰富的队列操作,比如 Multi-consumer or single-consumer dequeue Multi-producer or single-producer enqueue Bulk dequeue - Dequeues the specified count of objects if successful;