如何使用 Xamarin.Forms 在 Android X 和 iOS 上下载公开可用的文件?
How do I download a file on both Android X and iOS that is publicly available, using Xamarin.Forms?
查看此问题 xamarin/Essentials#1322,如何在 Android(版本 6-10、Api 23-29)和 iOS( 13.1+ 版),仅 public 可用(可共享给其他应用,例如 Microsoft Word)。我不需要给其他应用程序写权限,如果必须限制只读就可以了。
我得到以下异常:
[Bug] Android.OS.FileUriExposedException: file:///data/user/0/{AppBundleName}/cache/file.doc exposed beyond app through Intent.getData()
用下面的代码。
public static string GetCacheDataPath( string fileName ) => Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, fileName);
public static FileInfo SaveFile( string filename, Uri link )
{
using var client = new WebClient();
string path = GetCacheDataPath(filename);
DebugTools.PrintMessage(path);
client.DownloadFile(link, path);
return new FileInfo(path);
}
public async Task Test(Uri link)
{
LocalFile path = await SaveFile("file.doc", link).ConfigureAwait(true);
var url = new Uri($"ms-word://{path.FullName}", UriKind.Absolute);
await Xamarin.Essentials.Launcher.OpenAsync(url).ConfigureAwait(true);
}
通过这个 ,我创建了一个 FileService 接口,它可以处理本地私人文件,但我无法共享这些文件。以Android Q (10 / Api 29)开头,以下已弃用。
string path = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath; // deprecated
我得到以下异常:
System.UnauthorizedAccessException: Access to the path '/storage/emulated/0/Download/file.doc' is denied. ---> System.IO.IOException: Permission denied
我还没有找到任何方法来为 Android 10 和 Xamarin.Forms 获取 public 路径。我查看了 Android Docs for Content providers,但它在 Java 中,但我还不能让它在 C# 中工作。
如有任何帮助,我们将不胜感激。
我确实找到了 Solution
Found a fix
For Android
public Task<System.IO.FileInfo> DownloadFile( Uri link, string fileName )
{
if ( link is null )
throw new ArgumentNullException(nameof(link));
using System.Net.WebClient client = new System.Net.WebClient();
// MainActivity is the class that loads the application.
// MainActivity.Instance is a property that you set "Instance = this;" inside of OnCreate.
Java.IO.File root = MainActivity.Instance.GetExternalFilesDir(MediaStore.Downloads.ContentType);
string path = Path.Combine(root.AbsolutePath, fileName);
client.DownloadFile(link, path);
return Task.FromResult(new System.IO.FileInfo(path));
}
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
...
Instance = this;
...
}
...
}
For iOS
public Task<System.IO.FileInfo> DownloadFile( Uri link, string fileName )
{
if ( link is null )
throw new ArgumentNullException(nameof(link));
using System.Net.WebClient client = new System.Net.WebClient();
string path = Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, fileName)
client.DownloadFile(link, path);
return Task.FromResult(new System.IO.FileInfo(path));
}
public async Task Share()
{
// back in shared project, choose a file name and pass the link.
System.IO.FileInfo info = await DependencyService.Get<IDownload>().DownloadFile(new Uri("<enter site>", "file.doc").ConfigureAwait(true);
ShareFile shareFile = new ShareFile(info.FullName, "doc"); // enter the file type / extension.
var request = new ShareFileRequest("Choose the App to open the file", shareFile);
await Xamarin.Essentials.Share.RequestAsync(request).ConfigureAwait(true);
}
Note that for iOS, due to Apple's infinite wisdom... I cannot share the file directly with another app as I can on Android. Sandboxing is good for security but in this case, how they implemented it, it limits options. Both Applications must be pre-registered / pre-allocated in an "App Group" to share files directly. See this Article and the Apple Docs for more information.
查看此问题 xamarin/Essentials#1322,如何在 Android(版本 6-10、Api 23-29)和 iOS( 13.1+ 版),仅 public 可用(可共享给其他应用,例如 Microsoft Word)。我不需要给其他应用程序写权限,如果必须限制只读就可以了。
我得到以下异常:
[Bug] Android.OS.FileUriExposedException: file:///data/user/0/{AppBundleName}/cache/file.doc exposed beyond app through Intent.getData()
用下面的代码。
public static string GetCacheDataPath( string fileName ) => Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, fileName);
public static FileInfo SaveFile( string filename, Uri link )
{
using var client = new WebClient();
string path = GetCacheDataPath(filename);
DebugTools.PrintMessage(path);
client.DownloadFile(link, path);
return new FileInfo(path);
}
public async Task Test(Uri link)
{
LocalFile path = await SaveFile("file.doc", link).ConfigureAwait(true);
var url = new Uri($"ms-word://{path.FullName}", UriKind.Absolute);
await Xamarin.Essentials.Launcher.OpenAsync(url).ConfigureAwait(true);
}
通过这个
string path = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath; // deprecated
我得到以下异常:
System.UnauthorizedAccessException: Access to the path '/storage/emulated/0/Download/file.doc' is denied. ---> System.IO.IOException: Permission denied
我还没有找到任何方法来为 Android 10 和 Xamarin.Forms 获取 public 路径。我查看了 Android Docs for Content providers,但它在 Java 中,但我还不能让它在 C# 中工作。
如有任何帮助,我们将不胜感激。
我确实找到了 Solution
Found a fix
For Android
public Task<System.IO.FileInfo> DownloadFile( Uri link, string fileName ) { if ( link is null ) throw new ArgumentNullException(nameof(link)); using System.Net.WebClient client = new System.Net.WebClient(); // MainActivity is the class that loads the application. // MainActivity.Instance is a property that you set "Instance = this;" inside of OnCreate. Java.IO.File root = MainActivity.Instance.GetExternalFilesDir(MediaStore.Downloads.ContentType); string path = Path.Combine(root.AbsolutePath, fileName); client.DownloadFile(link, path); return Task.FromResult(new System.IO.FileInfo(path)); } public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { internal static MainActivity Instance { get; private set; } protected override void OnCreate(Bundle savedInstanceState) { ... Instance = this; ... } ... }
For iOS
public Task<System.IO.FileInfo> DownloadFile( Uri link, string fileName ) { if ( link is null ) throw new ArgumentNullException(nameof(link)); using System.Net.WebClient client = new System.Net.WebClient(); string path = Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, fileName) client.DownloadFile(link, path); return Task.FromResult(new System.IO.FileInfo(path)); } public async Task Share() { // back in shared project, choose a file name and pass the link. System.IO.FileInfo info = await DependencyService.Get<IDownload>().DownloadFile(new Uri("<enter site>", "file.doc").ConfigureAwait(true); ShareFile shareFile = new ShareFile(info.FullName, "doc"); // enter the file type / extension. var request = new ShareFileRequest("Choose the App to open the file", shareFile); await Xamarin.Essentials.Share.RequestAsync(request).ConfigureAwait(true); }
Note that for iOS, due to Apple's infinite wisdom... I cannot share the file directly with another app as I can on Android. Sandboxing is good for security but in this case, how they implemented it, it limits options. Both Applications must be pre-registered / pre-allocated in an "App Group" to share files directly. See this Article and the Apple Docs for more information.