问题
I am trying to open a pdf in a 3rd party app on the android device, but it's not possible. The applicaiton is built in xamarin forms with a shared project. I have used the solution provided here as a basis: Opening a PDF with Xamarin Forms.
//I changed the interface to a byte array because I want to download the pdf
public interface IDocumentView
{
void DocumentView(byte[] bytes,string name, string title);
}
//In my android project:
[assembly: Xamarin.Forms.Dependency(typeof(DocumentView))]
namespace FMSXMobileApp.Droid
{
public class DocumentView : IDocumentView
{
void IDocumentView.DocumentView(byte[] bytes, string name, string title)
{
var context = Xamarin.Forms.Forms.Context;
try
{
File extFile = new File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads), name);
File extDir = extFile.ParentFile;
// Copy file to external storage to allow other apps to access ist
if (System.IO.File.Exists(extFile.AbsolutePath))
System.IO.File.Delete(extFile.AbsolutePath);
System.IO.File.WriteAllBytes(extFile.AbsolutePath, bytes);
String mime = MimeTypeMap.GetFileExtensionFromUrl(extFile.AbsolutePath);
// if copying was successful, start Intent for opening this file
if (System.IO.File.Exists(extFile.AbsolutePath))
{
Intent intent = new Intent();
intent.SetAction(Android.Content.Intent.ActionView);
intent.SetDataAndType(Android.Net.Uri.FromFile(extFile), mime);
context.ApplicationContext.StartActivity(intent);
}
}
catch (ActivityNotFoundException anfe)
{
// android could not find a suitable app for this file
var alert = new AlertDialog.Builder(context);
alert.SetTitle("Error");
alert.SetMessage("No suitable app found to open this file");
alert.SetCancelable(false);
alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
alert.Show();
}
catch (Exception ex)
{
// another exception
var alert = new AlertDialog.Builder(context);
alert.SetTitle("Error");
alert.SetMessage("Error when opening document");
alert.SetCancelable(false);
alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
alert.Show();
}
}
}
}
//From my xamarin forms shared project I call the next statement:
DependencyService.Get<IDocumentView>().DocumentView(doc.File, "test.pdf", "Title of the view");
//In the xml folder of my android project (file_paths.xml):
<?xml version="1.0" encoding="utf-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="root" path="/"/>
<external-files-path name="files" path="files" />
</paths>
//in the manifest I added:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
The applicaiton fails at this line:
System.IO.File.WriteAllBytes(extFile.AbsolutePath, bytes);
But the code doesn't run I get the error message: Error when opening document. What am I doing wrong? I'm using android 9.0 sdk 28.
Here is the stack trace:
System.UnauthorizedAccessException: Access to the path "/storage/emulated/0/Download/test.pdf" is denied.
at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x001b7] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) [0x00000] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at (wrapper remoting-invoke-with-check) System.IO.FileStream..ctor(string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare)
at System.IO.File.InternalWriteAllBytes (System.String path, System.Byte[] bytes) [0x00000] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at System.IO.File.WriteAllBytes (System.String path, System.Byte[] bytes) [0x00039] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at FMSXMobileApp.Droid.DocumentView.FMSXMobileApp.Interfaces.IDocumentView.DocumentView (System.Byte[] bytes, System.String name, System.String title) [0x0003d] in D:\FMSXAPP\FMSXMobileApp\FMSXMobileApp.Android\DocumentView.cs:28
回答1:
As CrazyTim answered in https://stackoverflow.com/a/24796126/11992780
There are 4 scenarios :
- The caller does not have the required permission.
- The file is an executable file that is in use.
- Path is a directory.
- Path specified a read-only file.
回答2:
Finally I have found a solution that worked for me. The problem was that my app needed to request access to storage. I used this library for the requests https://www.nuget.org/packages/Plugin.Permissions/.
This is my code from the documents.xaml page:
var status = await Utils.CheckPermissions(Permission.Storage);
if (status == PermissionStatus.Denied) return;
DependencyService.Get<IDocumentView>().DocumentView(doc.File, "test.pdf", "Title of the view");
In the MainActivity of my .android project I had to add this code:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
This is my new DocumentView in mij .android project:
[assembly: Xamarin.Forms.Dependency(typeof(DocumentView))]
namespace FMSXMobileApp.Droid
{
public class DocumentView : IDocumentView
{
void IDocumentView.DocumentView(byte[] bytes, string name, string title)
{
var context = Android.App.Application.Context;
try
{
var filePath = CommonLibrary.Helpers.FileHelper.WriteFileFromByteArray(bytes, Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).Path, "fmsxapp.pdf");
var mime = MimeTypeMap.GetFileExtensionFromUrl(filePath);
// if copying was successful, start Intent for opening this file
if (System.IO.File.Exists(filePath))
{
Android.Net.Uri pdfPath = FileProvider.GetUriForFile(context, "com.mydomain.fileprovider", new Java.IO.File(filePath));
context.GrantUriPermission(context.PackageName, pdfPath, ActivityFlags.GrantReadUriPermission);
Intent intent = new Intent();
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
intent.SetAction(Android.Content.Intent.ActionView);
intent.SetDataAndType(pdfPath, $"application/{mime}");
context.StartActivity(intent);
}
}
catch (ActivityNotFoundException anfe)
{
// android could not find a suitable app for this file
var alert = new AlertDialog.Builder(context);
alert.SetTitle("Error");
alert.SetMessage("No suitable app found to open this file");
alert.SetCancelable(false);
alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
alert.Show();
}
catch (Exception ex)
{
// another exception
var alert = new AlertDialog.Builder(context);
alert.SetTitle("Error");
alert.SetMessage("Error when opening document");
alert.SetCancelable(false);
alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
alert.Show();
}
}
}
}
I didn't change anything in the manifest file. I did change my file_paths.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="root" path="."/>
<external-files-path name="files" path="files" />
<external-path name="external_files" path="."/>
</paths>
After this modifications it worked like a charm. I hope that this may help someone who is also struggeling to get it work.
来源:https://stackoverflow.com/questions/57695141/open-pdf-by-3rd-party-app-in-xamarin-forms-error-error-when-opening-document