uni-app 系统公共目的下的文件不支持移动和拷贝到沙盒目录?

uni-app 系统公共目的下的文件不支持移动和拷贝到沙盒目录?

开发环境 版本号 项目创建方式
HBuilderX 3.3.3 云端

示例代码:

moveCopyFileToDst: function(files, method, succCallback, errCallback) {  
    var _this = this,  
        result = [];  

    function copyfile(fileEntry, dstEntry, fileName, callback) {  
        fileEntry.copyTo(dstEntry, fileName, function(entry) {  
            callback(entry)  
        }, function(e) {  
            console.log(e.message + ' 有些文件路径不存在或无法复制')  
            errCallback(e.message)  
        })  
    }  

    function movefile(fileEntry, dstEntry, fileName, callback) {  
        console.log('movefile moveTo fileEntry.fullPath', fileEntry.fullPath)  
        console.log('movefile moveTo dstEntry.name', dstEntry.name)  
        console.log('movefile moveTo dstEntry.fullPath', dstEntry.fullPath)  
        console.log('movefile moveTo fileName', fileName)  
        fileEntry.moveTo(dstEntry, fileName, function(entry) {  
            callback(entry)  
        }, function(e) {  
            console.log(e.message)  
            errCallback(e.message)  
        })  
    }  

    /**  
     * 递归生成文件名  
     * @param {Object} fileName  
     * @param {Object} index  
     * @param {Object} callback  
     */  
    function recursionCreateFileName(fileName, dstPath, index, callback) {  
        //目标文件存在,这行会与if (_this.fileExists(dstPath + newFileName)) {重复判断  
        // if (_this.fileExists(dstPath + fileName)) {  
        //生成一个新文件名: abc.txt abc(0).txt abc(1).txt  
        var split = fileName.split('.'),  
            newFileName = split[0] + '(' + index + ').' + split[1];  
        //再判断新生成的文件名是否存在,如果存在重新调用自己  
        if (_this.fileExists(dstPath + newFileName)) {  
            recursionCreateFileName(fileName, dstPath, index + 1, callback)  
        } else {  
            //不存在,返回新文件名  
            callback(newFileName)  
        }  
        // } else {  
        //  callback(fileName)  
        // }  
    }  

    function recursion(files, index) {  
        if (index == files.length) {  
            return succCallback(result)  
        }  
        var originalFile = files[index],  
            fileSplit = originalFile.split('/'),  
            fileName = fileSplit[fileSplit.length - 1],  
            dstEntry = _this.getFileDirEntry(fileName);  

        console.log('原文件路径: ' + originalFile)  
        console.log('原文件名: ' + fileName)  
        console.log('目标文件目录: ' + dstEntry.fullPath + fileName)  
        console.log('目标文件路径: ' + dstEntry.name)  

        plus.io.resolveLocalFileSystemURL(dstEntry.fullPath + fileName, function(dstFileEntry) {  
            console.log('目标文件存在!' + dstFileEntry.fullPath)  
            plus.io.resolveLocalFileSystemURL(originalFile, function(fileEntry) {  
                if (method === 'move') {  
                    recursionCreateFileName(fileName, dstEntry.fullPath, 0, function(filename) {  
                        movefile(fileEntry, dstEntry, filename, function(ent) {  
                            result.push(ent.fullPath);  
                            recursion(index + 1)  
                        })  
                    })  
                }  
                if (method === 'copy') {  
                    console.log('dstEntry copy path: ' + dstEntry.fullPath)  
                    recursionCreateFileName(fileName, dstEntry.fullPath, 0, function(filename) {  
                        copyfile(fileEntry, dstEntry, filename, function(ent) {  
                            result.push(ent.fullPath);  
                            recursion(index + 1)  
                        })  
                    })  
                }  
            })  
        }, function(e) {  
            console.log('目标文件不存在!')  
            plus.io.resolveLocalFileSystemURL(originalFile, function(fileEntry) {  
                if (method === 'move') {  
                    console.log('dstEntry move path: ' + dstEntry.fullPath + fileName)  
                    movefile(fileEntry, dstEntry, fileName, function(ent) {  
                        result.push(ent.fullPath);  
                        recursion(index + 1)  
                    })  
                }  
                if (method === 'copy') {  
                    console.log('dstEntry copy path: ' + dstEntry.fullPath + fileName)  
                    copyfile(fileEntry, dstEntry, fileName, function(ent) {  
                        result.push(ent.fullPath);  
                        recursion(index + 1)  
                    })  
                }  
            })  
        })  
    }  
    recursion(files, 0)  

}  

    plus.gallery.pick(function(res) {  
            console.log('actionSheet pick files: ' + JSON.stringify(res))  
            _this.moveCopyFileToDst(res.files, 'move', function(res) {  

            }, function(err) {  

            })  
                },  
            function(e) {  
                console.log(JSON.stringify(e));  
                plus.nativeUI.closeWaiting()  
            }, config)

操作步骤:

  1. pick 选择图片。
  2. 勾选最下面的“原图”。
  3. 选图片。
  4. 判断 _doc/images 下是否有该文件。如果不存在就移动到 _doc/images 下,存在就重新启个名字再移动到 _doc/images 下。

预期结果:

移动或复制成功

实际结果:

targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径。请更改为应用运行路径!具体请看: https://ask.dcloud.net.cn/article/36199

bug描述:

系统公共目的下的文件 file:///storage/emulated/0/Download/1639293586409.jpg 不支持移动和拷贝到沙盒目录 /storage/emulated/0/Android/data/xx.xx.com/apps/H5AE01F30/doc/images/ 中?


更多关于uni-app 系统公共目的下的文件不支持移动和拷贝到沙盒目录?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

8 回复

公共目的下的文件不支持移动到应用沙盒目录。正如错误信息提示

更多关于uni-app 系统公共目的下的文件不支持移动和拷贝到沙盒目录?的实战教程也可以访问 https://www.itying.com/category-93-b0.html


您好,请问我怎么操作才行?压缩吗?

复制可以吗?

回复 矩信: plus.io不支持 你可以尝试使用uni原生插件来实现。直接使用原生代码实现

回复 DCloud_Android_ST: 我是 html plu5+ 开发的,也能用 uni 原生插件吗?请问怎么用?

刚才试了一下,move 不可以,copy 可以

换策略了,如果从相册返回的文件路径包含 uniapp_temp/compressed 说明不是原图是压缩后的,这个 moveTo() 支持,反之说明是原图,就用 copyTo(),uniapp_temp/compressed 用 moveTo 就对了,要不然会产生大量垃圾,除非自己专门写代码维护。

是的,在 Android 10(API 29)及以上版本,由于系统引入了作用域存储(Scoped Storage)的限制,应用无法直接通过文件路径(如 file:///storage/emulated/0/Download/...)访问和操作系统公共目录下的文件。这包括移动(moveTo)和复制(copyTo)操作。

您遇到的错误信息“系统公共目的下的文件不支持移动和拷贝到沙盒目录”正是这个原因。当 targetSdkVersion 设置为 29 或更高时,应用对公共目录的访问权限受到了更严格的管控。

核心问题与解决方案:

  1. 问题根源:您通过 plus.gallery.pick 获取到的文件 URI(如 file:///storage/emulated/0/Download/1639293586409.jpg)在 Android 10+ 上,应用仅拥有临时的读取权限。您无法直接使用 plus.iomoveTocopyTo 方法将其操作到应用的私有沙盒目录(_doc)。
  2. 正确做法:必须使用 “先读取,再写入” 的方式。即:
    • 通过获取到的文件 URI,使用 plus.io 的相关 API 读取文件内容(例如,转换为 BlobArrayBuffer)。
    • 然后,在应用沙盒目录(如 _doc/images)下创建一个新文件。
    • 最后,将读取到的内容写入到这个新创建的文件中。

代码修改建议(关键部分):

替换您 moveCopyFileToDst 函数中对于公共目录文件的操作逻辑。以下是一个处理 copy 操作的示例思路:

// 假设 originalFile 是类似 `file:///storage/emulated/0/Download/xxx.jpg` 的路径
function copyPublicFileToSandbox(originalFile, dstDirEntry, fileName, callback) {
    // 1. 读取公共目录文件内容
    plus.io.resolveLocalFileSystemURL(originalFile, function(fileEntry) {
        fileEntry.file(function(file) {
            var reader = new FileReader();
            reader.onloadend = function(e) {
                // 2. 在沙盒目标目录创建新文件
                dstDirEntry.getFile(fileName, { create: true }, function(newFileEntry) {
                    // 3. 将内容写入新文件
                    newFileEntry.createWriter(function(writer) {
                        writer.onwriteend = function(e) {
                            callback(newFileEntry); // 操作成功,返回新文件Entry
                        };
                        writer.onerror = function(e) {
                            errCallback('写入文件失败: ' + e.toString());
                        };
                        // 将读取到的ArrayBuffer数据写入
                        var blob = new Blob([reader.result]);
                        writer.write(blob);
                    }, function(e) {
                        errCallback('创建Writer失败: ' + e.message);
                    });
                }, function(e) {
                    errCallback('创建文件失败: ' + e.message);
                });
            };
            reader.onerror = function(e) {
                errCallback('读取文件失败: ' + e.toString());
            };
            reader.readAsArrayBuffer(file);
        }, function(e) {
            errCallback('获取File对象失败: ' + e.message);
        });
    }, function(e) {
        errCallback('解析源文件路径失败: ' + e.message);
    });
}
回到顶部