Android 从内部存储器创建和共享文件
我的目标是在内部存储上创建一个XML文件,然后通过共享服务器发送 我能够使用以下代码创建XML文件Android 从内部存储器创建和共享文件,android,android-contentprovider,android-file,Android,Android Contentprovider,Android File,我的目标是在内部存储上创建一个XML文件,然后通过共享服务器发送 我能够使用以下代码创建XML文件 FileOutputStream outputStream = context.openFileOutput(fileName, Context.MODE_WORLD_READABLE); PrintStream printStream = new PrintStream(outputStream); String xml = this.writeXml(); // get XML here pr
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(…)内部抛出的异常
,但我不明白为什么会抛出它
E/活动线程(572):活动
com.android.internal.app.chooseActivity已泄漏IntentReceiver
com.android.internal.app.ResolverActivity$1@4148d658那是
最初在这里注册。你是不是错过了一个电话
取消注册接收器()
我研究过这个错误,没有发现任何明显的错误。这两个链接都表明我需要注销接收者
AlarmManager
,不需要应用程序注册/注销
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;
}
可以通过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>
确保已将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为您的私有文件获取InputStream(content://com.example.prov/myfile.xml)
对于一个简单的测试,请从一个单独的应用程序调用内容提供商,类似于以下内容
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);
}
}
私有类MyTask扩展了AsyncTask{
@凌驾
受保护的字符串doInBackground(字符串…参数){
Uri=Uri.parse(“content://com.example.prov/myfile.xml");
InputStream=null;
StringBuilder结果=新建StringBuilder();
试一试{
is=getApplicationContext().getContentResolver().openInputStream(uri);
BufferedReader r=新的BufferedReader(新的InputStreamReader(is));
弦线;
而((line=r.readLine())!=null){
结果。追加(行);
}
}catch(filenotfounde异常){
e、 printStackTrace();
}捕获(IOE异常){
e、 printStackTrace();
}最后{
尝试{if(is!=null)is.close();}catch(IOException e){}
}
返回result.toString();
}
@凌驾
受保护的void onPostExecute(字符串结果){
Toast.makeText(CallerActivity.this,result,Toast.LENGTH_LONG.show();
super.onPostExecute(结果);
}
}
如果您需要允许其他应用程序查看您的应用程序的私有文件(用于共享或其他),您可能可以节省一些时间,只需使用v4 compat library的,因此我认为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>
然后,当我有这个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);
PackageManager-PackageManager=getPackageManager();
List List=packageManager.QueryInputActivities(仅CameraInput、packageManager.MATCH_默认值_);
if(list.size()<1){
返回;
}
字符串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);
setFlags(Intent.FLAG\u GRANT\u WRITE\u URI\u权限);
cameraIntent.setFlags(Intent.FLAG\u GRANT\u READ\u URI\u PERMISSION);
startActivityForResult(摄像机帐篷,从摄像机获取);
我留下了照相机的代码,因为我不想再拿其他一些我没有做过的例子。但通过这种方式,您可以将权限附加到URI本身
相机的问题是,我可以通过ClipData设置它,然后额外设置权限。我猜在你的情况下,当你将文件附加到一封电子邮件时,你只需要FLAG\u GRANT\u READ\u URI\u权限
以下是我根据在那里找到的信息发布的所有帖子的FileProvider帮助信息。不过,在查找照相机应用程序的软件包信息时遇到了一些问题
希望有帮助。这是我正在使用的: 我结合了一些答案,使用了当前的AndroidX Doku: 基本流程:您可以更改清单,使其他应用程序能够访问您的本地文件。允许从外部访问的文件路径位于res/xml/filepath.xml中。共享时,创建共享的意图并设置标志
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path path="/" name="allfiles" />
<files-path path="tmp/" name="tmp" />
</paths>
Uri contentUri = FileProvider.getUriForFile(context, "com.your.app.package", sharedFile);
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);
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.YOUR.APP.PACKAGE.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path path="share/" name="share" />
</paths>
public void ShareFiles(Activity activity, List<File> files, String header, String body) {
ArrayList<Uri> uriList = new ArrayList<>();
if(files != null) {
for (File f : files) {
if (f.exists()) {
File file_in_share = copy_File(f, ShareDir);
if(file_in_share == null)
continue;
// Use the FileProvider to get a content URI
try {
Uri fileUri = FileProvider.getUriForFile(
activity,
"com.YOUR.APP.PACKAGE.fileprovider",
file_in_share);
uriList.add(fileUri);
} catch (IllegalArgumentException e) {
Log.e("File Selector",
"The selected file can't be shared: " + f.toString());
}
}
}
}
if(uriList.size() == 0)
{
Log.w("StorageModule", "ShareFiles: no files to share");
return;
}
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("text/html");
intent.putExtra(Intent.EXTRA_SUBJECT, header);
intent.putExtra(Intent.EXTRA_TEXT, body);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
activity.startActivity(Intent.createChooser(intent, "Share Files"));
}