android .Net MAUI写入连接的计算机可以看到的文件

tnkciper  于 2023-03-16  发布在  Android
关注(0)|答案(2)|浏览(347)

我正试图在Android设备上写一个文件。然后我希望能够轻松地将该文件传输到Windows计算机上进行查看和分析。
不用说,我在做这件事时遇到了很多问题。我使用的是.Net MAUI,我的Android设备是运行Android 13的三星Galaxy Tab A7 Lite。
我正在运行下面的代码来编写一个测试文件。我还插入了一些代码作为一个健全性检查,以立即读取我编写的文件:

string target_file = System.IO.Path.Combine(FileSystem.Current.AppDataDirectory, "test_file.txt");
using (FileStream output_stream = System.IO.File.OpenWrite(target_file))
{
    using (StreamWriter stream_writer = new StreamWriter(output_stream))
    {
        stream_writer.Write("hello, world!");
    }
}

using (FileStream input_stream = System.IO.File.OpenRead(target_file))
{
    using (StreamReader stream_reader = new StreamReader(input_stream))
    {
        var input_text = stream_reader.ReadToEnd();
    }
}

这段代码是 * 工作 *。它 * 是 * 写一个文件,我 * 是 * 能够读取该文件。问题是,我不能看到这个文件以外的任何地方的应用程序。我不能看到它在Android的“我的文件”应用程序在三星平板电脑,也不能看到它在Windows文件资源管理器当我连接到我的Android平板电脑的PC。
该文件应该位于以下路径:“/数据/用户/0/[com.公司名称.应用程序名称]/文件/测试文件.txt”
根据以下Stack Overflow帖子中的评论,“解决方案”是保存到“共享”文件夹,因为显然私有应用数据文件夹无法使用Windows文件资源管理器查看:

  1. Creating and Writing to a text file on Android .NET MAUI
  2. .NET MAUI writing file in android to folder that then can be accessed in windows file explorer
    不幸的是,这个帖子上的答案 * 不 * 工作:
    .NET MAUI writing file in android to folder that then can be accessed in windows file explorer
    它再次将保存的文件定向到应用的私有数据文件夹中,具体来说,它会尝试保存到以下路径:
    “/存储/模拟/0/Android/数据/[com.公司名称.应用程序名称]/文件/文档”
    已经提出的一个建议是使用.Net MAUI FileSaver(https://learn.microsoft.com/en-us/dotnet/communitytoolkit/maui/essentials/file-saver?tabs=android)。虽然这 * 看起来 * 允许保存到共享文件夹(我还没有完全测试它),它需要用户交互来实际选择文件的保存位置,这在这种情况下是不可接受的。我的应用程序需要能够随时将日志文件保存到一个非常特定的文件夹中(没有用户交互),然后周期性地,用户需要能够将那些文件传输到计算机以供查看/分析。
    对于如何解决这个问题有什么建议吗?
fdbelqdn

fdbelqdn1#

对于那些想知道如何将文件写入公共文件夹的人,尤其是使用.Net MAUI时,我给出了一个解决方法。
首先,我的主要资源是这两个职位:

  1. Storage Access Framework persist permissions not working
  2. Android Studio, Kiosk mode, Single-Purpose Devices, Lock Task mode
    以下文档也很有帮助:
  3. https://developer.android.com/reference/android/content/Intent#FLAG_GRANT_PREFIX_URI_PERMISSION
    1.获取持久用户权限(安卓. net. Uri,%20int)
    1.内容解析器#获取持久用户权限()
    无论如何,第1步是定义一个分部类,然后可以基于每个平台重新定义它。
namespace MyProjectName.CrossPlatformComponents
{
    public partial class ChooseLogFolder_CrossPlatform
    {
        public static int REQUEST_TREE = 85;

        public static partial void RequestSelectLogFolder();

        public static partial void OpenLogFileForWriting(string file_name, string file_contents);

        public static partial bool HasFolderBeenSelectedAndPermissionsGiven ();
    }
}

如您所见,我基本上希望实现3个函数:
(1)一个让用户选择文件夹的功能-这个文件夹将具有 persistable 权限,因此我们可以 always 从它读取/向它写入。它将是一个 public 文件夹,而不是应用程序私有的文件夹。(2)一个将数据实际写入文件的功能(3)一个检查并查看是否已选择文件夹位置并已授予权限的功能。
现在,对于.Net MAUI,假设你想把你的应用部署到多个平台,你需要为每个平台定义这些函数。为了这篇文章,我们只关注Android。
下面是我在Android上实现的这些函数:

namespace MyProjectName.CrossPlatformComponents
{
    public partial class ChooseLogFolder_CrossPlatform
    {
        public static partial void RequestSelectLogFolder()
        {
            var current_activity = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity;   
            var intent = new Android.Content.Intent(Android.Content.Intent.ActionOpenDocumentTree);
            intent.AddFlags(Android.Content.ActivityFlags.GrantReadUriPermission | 
                            Android.Content.ActivityFlags.GrantWriteUriPermission | 
                            Android.Content.ActivityFlags.GrantPersistableUriPermission | 
                            Android.Content.ActivityFlags.GrantPrefixUriPermission);
            current_activity.StartActivityForResult(intent, REQUEST_TREE);
        }

        public static partial void OpenLogFileForWriting(string file_name, string file_contents)
        {
            var current_activity = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity;

            List<UriPermission> permissions = current_activity.ContentResolver.PersistedUriPermissions.ToList();
            if (permissions != null && permissions.Count > 0 )
            {
                DocumentFile log_folder = DocumentFile.FromTreeUri(current_activity, permissions[0].Uri);
                DocumentFile log_file = log_folder.CreateFile(MimeTypeMap.Singleton.GetMimeTypeFromExtension("txt"), file_name);
                ParcelFileDescriptor pfd = current_activity.ContentResolver.OpenFileDescriptor(log_file.Uri, "w");
                FileOutputStream file_output_stream = new FileOutputStream(pfd.FileDescriptor);
                file_output_stream.Write(Encoding.UTF8.GetBytes(file_contents));
                file_output_stream.Close();
            }
        }

        public static partial bool HasFolderBeenSelectedAndPermissionsGiven()
        {
            var current_activity = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity;

            List<UriPermission> permissions = current_activity.ContentResolver.PersistedUriPermissions.ToList();
            return (permissions != null && permissions.Count > 0);
        }
    }
}

接下来,在实际的Android Activity类中,我们需要处理RequestSelectLogFolder函数的结果-因为它调用StartActivityForResult。因此,在我们的Android Activity中,我们需要覆盖OnActivityResult,然后做一些工作来处理该结果。

public class MainActivity : MauiAppCompatActivity
{
    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);

        if (resultCode == Result.Ok)
        {
            if (requestCode == ChooseLogFolder_CrossPlatform.REQUEST_TREE)
            {
                // The result data contains a URI for the document or directory that the user selected.
                if (data != null)
                {
                    Android.Net.Uri uri = data.Data;
                    var flags = data.Flags & (Android.Content.ActivityFlags.GrantReadUriPermission | Android.Content.ActivityFlags.GrantWriteUriPermission);

                    //Take the persistable URI permissions (so that they actually persist)
                    this.ContentResolver.TakePersistableUriPermission(uri, flags);
                }
            }
        }
    }
}

最后,在完成所有这些之后,我们应该可以开始了。我们将能够在Android设备上的用户指定的公共目录中读写文件,并且这些权限将在应用重新启动和设备重新启动后保持不变。
因此,为了实际使用这段代码,我可以做如下的事情。让我们假设用户按下了UI中的按钮,并且我想在用户按下按钮时将数据写入一个文件。这可以是我的按钮事件处理程序中的代码:

if (ChooseLogFolder_CrossPlatform.HasFolderBeenSelectedAndPermissionsGiven())
{
    ChooseLogFolder_CrossPlatform.OpenLogFileForWriting("test_file.txt", "hello, world!");
}
else
{
    ChooseLogFolder_CrossPlatform.RequestSelectLogFolder();
}

好了,就这些了,希望这对以后的人有帮助。

0s7z1bwu

0s7z1bwu2#

实际上,由于访问限制,您无法更改下载文件夹。此外,您无法从Android/data/目录及其所有子目录中获取数据。
更多信息请参考Storage updates in Android 11

相关问题