Android:通过加密打开将文件保存到可移动SD卡

时间:2017-10-09 16:45:11

标签: android mime-types android-sdcard storage-access-framework

我已经实现了一种方法,供用户使用Storage Access Framework将我的应用创建的文件保存/导出到任何位置。架构是:

  1. 使用Intent.ACTION_OPEN_DOCUMENT_TREE让用户选择目录
  2. 使用DocumentsContract在该目录中创建文件
  3. 将数据写入此文件
  4. 该文件是包含自定义数据的自定义文件扩展名,因此不是标准文件类型或mime类型。 我正在使用插有SD卡的三星Galaxy S7。所以它不支持Adoptable Storage。对于测试/开发,我使用My Files系统应用程序打开文件和带有MTP的Windows PC从设备中提取文件。

    此架构/代码在以下情况下运行良好:

    1. 任何内部存储位置
    2. 辅助外部存储,未加密(可移动SD卡)
    3. 如果SD卡已加密,我开始遇到奇怪的问题。

      问题1:“我的文件”系统应用程序将文件视为大小为0字节,不会打开它。

      问题2:Windows PC也将该文件视为大小为0字节,不会通过MTP将其复制到我的PC上。

      奇怪的是,ES文件资源管理器可以看到该文件并将其打开。 DropBox可以看到该文件并上传它。如果SD卡未加密,则不存在上述问题1和2。

      那么这里发生了什么?我已经尝试了很多东西来排除故障。我的理论是,处理MIME类型和内容URI时会出现一些问题,这些URI在加密或关闭时表现不同 - 特别是关于我的文件和MTP。为什么ES文件资源管理器在查看此文件并打开它时没有问题?

      以下是相关的代码段:

      private void selectStoragePathExtended() {
          Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
          startActivityForResult(intent, REQUEST_CODE_OPEN_DIRECTORY);
      }
      
      @Override
      public void onActivityResult(int requestCode, int resultCode, Intent data) {
          if (requestCode == REQUEST_CODE_OPEN_DIRECTORY) {
              if (resultCode == Activity.RESULT_OK) {
                  mLocalStorageUri = DocumentsContract.buildDocumentUriUsingTree(data.getData(), DocumentsContract.getTreeDocumentId(data.getData()));
              }
          }
      }
      // This will get called with mLocalStorageUri and a File stored in the app's "ExternalFilesDir"
      public static void copyFile(ContentResolver cr, File sourceFile, Uri destFolderUri) throws IOException {
          if (!sourceFile.exists()) {
              return;
          }
      
          FileChannel source = null;
          FileChannel destination = null;
      
          if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
              try {
                  source = new FileInputStream(sourceFile).getChannel();
                  Uri destUri = DocumentsContract.createDocument(cr, destFolderUri, "*/*", sourceFile.getName());
      
                  ParcelFileDescriptor pfd = cr.openFileDescriptor(destUri, "w");
                  destination = new FileOutputStream(pfd.getFileDescriptor()).getChannel();
      
                  if (source.size() > FILE_COPY_MAX_BLOCK) {
                      // Transfer file in 128MB blocks
                      long position = 0;
                      while (destination.transferFrom(source, position, FILE_COPY_MAX_BLOCK) > 0) {
                          position += FILE_COPY_MAX_BLOCK;
                      }
                  } else {
                      long bytesCopied = destination.transferFrom(source, 0, source.size());
                      if (bytesCopied != source.size()) {
                          String errorMsg = String.format("Error: only %d out of %d bytes copied", bytesCopied, source.size());
                          throw new IOException(errorMsg);
                      }
                  }
                  destination.close();
                  pfd.close();
              }
              finally {
                  if(source != null) {
                      source.close();
                  }
                  if(destination != null) {
                      destination.close();
                  }
              }
          }
      }
      

      是的我正在使用FileChannel的非标准文件复制方法,但我已经使用基本的FileOutputStreams和其他具有相同结果的方法进行了测试。此外,请记住,行为是正常的,并关闭SD卡加密。它实际上可以使用加密,但My Files和Windows MTP都无法读取文件。仅限第三方工具,如ES文件资源管理器和DropBox。

      有什么想法吗?必须是mime类型/ URI问题?

0 个答案:

没有答案