首页 > 解决方案 > 在 Delphi 10.3 Rio 中写入外部 SD 卡

问题描述

我编写了一个照片应用程序,将图像从外部源流式传输到平板电脑(三星 Galaxy TAB S)上的应用程序。我决定使用平板电脑中的外部 SD 卡作为存储,因为内部存储经常用完空间。另外,我需要将其删除以备份所有图像。

“读取” JPG 时一切正常,但在将 JPG 从流中“写入”到 SD 卡时,权限被拒绝。

我已经设置了WRITE_EXTERNAL_STORAGE权限,但这不适用于 Android SDK 25.2.5,因为 Android 更改了权限用于外部访问的方式。

fWriteStorage:=JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);

PermissionsService.RequestPermissions([fReadStorage, fWriteStorage], DisplayRationale);

我想要做的是调用 Android 系统文件夹选择器对话框以允许应用程序访问以写入 SD 卡。如此处所述:Android 5.0 及更高版本上的 SD 卡

有谁知道我如何在 Delphi 10.3 Rio 中调用 SD 卡的权限选择器?与包括 TotalCommander for Android 在内的所有应用程序类似,需要对外部 SD 卡进行写访问。

我现在再次尝试,使用以下代码,但仍然没有运气。我需要为图库创建新文件夹,但无法创建该文件夹。

  fWriteStorage := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);
  if PermissionsService.IsPermissionGranted(fWriteStorage) then
  begin
    applog('IsPermissionGranted: TRUE');
    if ForceDirectories(System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage')) then
      AppLog('GLOBAL_SDCARD Created')
    else
      AppLog('Can not create SD CARD folder ' + System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage'));
      PermissionsService.RequestPermissions([fWriteStorage],
    procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>)
    begin
      if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
      begin
        AppLog('Access Granted');
        if ForceDirectories(System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage')) then
          AppLog('GLOBAL_SDCARD Created')
        else
          AppLog('Can not create SD CARD folder ' + System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage'));

      end
      else
      begin
        AppLog('Access Denied');
      end;
    end);

我总是得到“授予访问权限”,但无法创建文件夹。

SD 卡不是平板电脑内部的,而是“可移动”的 microSD 卡,因为当画廊已满时我需要将其移除以备份到另一台设备 (PC/MAC)。卡的路径是/storage/2266-7298/。不同于使用标准目录返回的内容。

标签: androiddelphidelphi-10.3-rio

解决方案


您无需调用系统对话框即可获得访问权限。

使用 时PermissionsService.RequestPermissions(),您实际上需要等待 Android 回复授权或拒绝状态,然后才能尝试执行您请求权限的操作。

在您显示的代码中,您没有传递回调函数RequestPermissions()来获取该回复。因此,在向 SD 卡写入文件之前,您不知道用户是否实际授予了对 SD 卡的访问权限。

你需要更多这样的东西:

fWriteStorage := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);

...

if PermissionsService.IsPermissionGranted(fWriteStorage) then
begin
  // access previously granted, write files...
end
else
begin
  PermissionsService.RequestPermissions([fWriteStorage],
    procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>)
    begin
      if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
      begin
        // access granted, write files...
      end
      else
      begin
        // access denied, can't write files...
      end;
    end,
    DisplayRationale);
  end;
end;

请注意,如果您使用 Android 的Context.getExternalFilesDir()功能(由 Delphi 的方法包装),如果您正在写入属于您的应用程序包的文件夹,则System.IOUtils.TPath.GetSharedDocumentsPath()实际上不需要写入 SD 卡的权限:WRITE_EXTERNAL_STORAGE

从 开始Build.VERSION_CODES.KITKAT,读取或写入返回的路径不需要权限;调用应用程序始终可以访问它。这仅适用于为调用应用程序的包名称生成的路径。访问属于其他包的路径,Manifest.permission.WRITE_EXTERNAL_STORAGE和/或是Manifest.permission.READ_EXTERNAL_STORAGE必需的。

像 TotalCommander 这样的应用程序访问其他应用程序的文件,这就是它需要(READ|WRITE)_EXTERNAL_STORAGE. 您的应用程序可能不会,如果它只是读取/写入自己的文件以供自己使用。


推荐阅读