从内部存储创建和共享文件 [英] Create and Share a File from Internal Storage

查看:27
本文介绍了从内部存储创建和共享文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

My goal is to create a XML file on internal storage and then send it through the share Intent.

我可以使用此代码创建 XML 文件

I'm able to create a XML file using this code

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 来访问该文件

I'm stuck trying to retrieve a Uri to the output file in order to share it. I first tried to access the file by converting the file to a Uri

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

这会返回file:///data/data/com.my.package/files/myfile.xml,但我似乎无法将其附加到电子邮件、上传等内容中.

This returns file:///data/data/com.my.package/files/myfile.xml but I cannot appear to attach this to an email, upload, etc.

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

If I manually check the file length, it's proper and shows there is a reasonable file size.

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

Next I created a content provider and tried to reference the file and it isn't a valid handle to the file. The ContentProvider doesn't ever seem to be called a any point.

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

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

This returns content://com.my.package.provider/myfile.xml but I check the file and it's zero length.

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

How do I access files properly? Do I need to create the file with the content provider? If so, how?

更新

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

Here is the code I'm using to share. If I select Gmail, it does show as an attachment but when I send it gives an error Couldn't show attachment and the email that arrives has no attachment.

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(...) 内部抛出了一个 Exception,但我不知道为什么会抛出它.

I noticed that there is an Exception thrown here from inside createChooser(...), but I can't figure out why it's thrown.

E/ActivityThread(572):活动com.android.internal.app.ChooserActivity 泄露了 IntentReceivercom.android.internal.app.ResolverActivity$1@4148d658 那是最初注册在这里.你错过了一个电话取消注册接收器()?

E/ActivityThread(572): Activity com.android.internal.app.ChooserActivity has leaked IntentReceiver com.android.internal.app.ResolverActivity$1@4148d658 that was originally registered here. Are you missing a call to unregisterReceiver()?

我已经研究了这个错误,但没有发现任何明显的错误.这两个链接都表明我需要取消注册接收器.

I've researched this error and can't find anything obvious. Both of these links suggest that I need to unregister a receiver.

我有一个接收器设置,但它用于在别处设置的 AlarmManager,不需要应用程序注册/取消注册.

I have a receiver setup, but it's for an AlarmManager that is set elsewhere and doesn't require the app to register / unregister.

openFile(...) 的代码

如果需要,这里是我创建的内容提供程序.

In case it's needed, here is the content provider I've created.

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

推荐答案

可以通过 ContentProvider 公开存储在您的应用程序私有目录中的文件.这是我制作的一些示例代码,展示了如何创建可以执行此操作的内容提供程序.

It is possible to expose a file stored in your apps private directory via a ContentProvider. Here is some example code I made showing how to create a content provider that can do this.

清单

<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

In your ContentProvider override openFile to return the 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 文件复制到缓存目录

Make sure you have copied your xml file to the cache directory

    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

Now any other apps should be able to get an InputStream for your private file by using the content uri (content://com.example.prov/myfile.xml)

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

For a simple test, call the content provider from a seperate app similar to the following

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

这篇关于从内部存储创建和共享文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆