首页 > 解决方案 > 有没有办法在 Android 11 上将图像保存到没有 SAF 的 USB 记忆棒?

问题描述

我想将使用相机拍摄的图像从我的应用程序直接保存到插入手机的 USB 记忆棒中。有没有办法在不要求用户从 SAF 中选择目录的情况下做到这一点?我想使用范围存储,因为我的目标是 Android 11 及更高版本。

没有连接到设备的 SD 卡。

我可以使用此代码将文件存储在 Android 10 上的 USB 记忆棒上,但不能在 Android 11 上

// I pass the image taken from the camera by parameter
    private void legacyToUsb(File image){
        File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(getApplicationContext(), null);
        String path = null;
        for (File externalDir : externalStorageVolumes) {
            Log.d(TAG, "Found dir at : " + externalDir);
            path = externalDir.getPath();
        }
        if (path == null) {
            showToast("Failed to found mounted device");
            return;
        }
        String fileName = mDateFormat.format(System.currentTimeMillis()) + ".jpg";
        File destFile = new File(path, fileName);
        showToast("Going to save data to " + destFile.getPath());
        if (destFile == null)
            showToast("Failed to open file");
        FileOutputStream os;
        String msg = "Successfully saved to " + destFile.getPath();
        try{
            byte[] imageData = getBytesFromFile(image);
            os = new FileOutputStream(destFile);
            os.write(imageData);
        } catch (IOException e) {
            e.printStackTrace();
            msg = "Failed to save file : " + e.getMessage();
        }
        showToast(msg);
    }

但是,在 Android 11 上不起作用(我无法在 USB 记忆棒内创建文件),因为getExternalFilesDirs()没有给我 USB otg 的路径。

ContextCompat.getExternalFilesDirs(<context>)返回:

安卓 10

Found dir at : /storage/emulated/10/Android/data/com.example.MyApp/files
Found dir at : /storage/4406-15E5/Android/data/com.example.MyApp/files //I'm using this one

安卓 11

Found dir at : /storage/emulated/0/Android/data/com.example.MyApp/files 

据我了解,范围存储消除了通过这些功能访问可移动存储的可能性。所以我正在尝试另一种方式

    private void scopedToUsb(File image){
        ContentResolver resolver = getContentResolver();
        String fileName = mDateFormat.format(System.currentTimeMillis());

        ContentValues fileDetails = new ContentValues();
        fileDetails.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
        fileDetails.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        fileDetails.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
        fileDetails.put(MediaStore.Images.Media.IS_PENDING, 1);

        Set<String> volumeNames = MediaStore.getExternalVolumeNames(getApplicationContext());
        String storageVolume = null;
        // Get the first volume found here, it returns the hex with which the usb stick is identified
        // in my case 4406-15e5
        for (String volumeName : volumeNames){
            storageVolume = volumeName;
            Log.d(TAG, "Found volumeName : "+ volumeName);
            break;
        }
        // I have tried here MediaStore.VOLUME_EXTERNAL and MediaStore.VOLUME_EXTERNAL_PRIMARY
        // They both point to the same directory not in the usb stick
        Uri collection = MediaStore.Images.Media.getContentUri(storageVolume);
        Uri imageUri =  resolver.insert(collection, fileDetails);
        if(imageUri == null) {
            showToast("Failed to insert image row to ContentResolver");
            return;
        }
        Log.d(TAG, "Inserted, going to create ouput stream");
        OutputStream out = null;
        try {
            out = resolver.openOutputStream(imageUri);
            Bitmap bm = BitmapFactory.decodeFile(image.getAbsolutePath());
            bm.compress(Bitmap.CompressFormat.JPEG, 100, out);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
            showToast("Failed to sqve picture to Usb");
            return;
        }
        showToast("Successfully saved to " + collection.getPath());
    }

但我在这一行收到异常

Uri imageUri =  resolver.insert(collection, fileDetails);

例外

java.lang.IllegalStateException: Failed to create directory: /mnt/media_rw/4406-15E5/Pictures/.pending-1621349186-2021-05-11-16-46-26-671.jpg
        at android.os.Parcel.createExceptionOrNull(Parcel.java:2384)
        at android.os.Parcel.createException(Parcel.java:2360)
        at android.os.Parcel.readException(Parcel.java:2343)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
        at android.content.ContentProviderProxy.insert(ContentProviderNative.java:549)
        at android.content.ContentResolver.insert(ContentResolver.java:2177)
        at android.content.ContentResolver.insert(ContentResolver.java:2138)
        at com.example.MyApp.MainActivity.scopedToUsb(MainActivity.java:368)
        at com.example.MyApp.MainActivity.saveToUsb(MainActivity.java:294)
        at com.example.MyApp.MainActivity.access$000(MainActivity.java:72)
        at com.example.MyApp.MainActivity$1.onImageSaved(MainActivity.java:273)
        at androidx.camera.core.ImageCapture$2.onImageSaved(ImageCapture.java:844)
        at androidx.camera.core.ImageSaver.lambda$postSuccess$1$ImageSaver(ImageSaver.java:308)
        at androidx.camera.core.-$$Lambda$ImageSaver$29vxg6qyjKwPRXgWrIwgYVInWKE.run(Unknown Source:4)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

可能吗 ?我是否缺少任何许可?我应该强制使用存储访问框架吗?谢谢

标签: androidusbmediastoreandroid-11storage-access-framework

解决方案


推荐阅读