前言
在写一个复制和移动视频文件的项目中,遇到了要复制的问题,但是懒人精灵并没有提供移动和复制文件的方法。需要自己去实现。
先尝试了Ai使用“java.nio.file.Files”类,发现“java.nio.file.Paths.get”这个方法会出现参数报错:
Invalid method call. Invalid Parameters.
public static java.nio.file.Path java.nio.file.Paths.get(java.lang.String,java.lang.String[])
public static java.nio.file.Path java.nio.file.Paths.get(java.net.URI)
这个报错的核心原因是:安卓(Android)平台默认不支持 Java 标准的 java.nio.file 包(包括 Paths 和 Files 类)。
安卓有自己的文件系统 API(android.os.Environment、java.io.File、Context.getExternalFilesDir() 等),而 java.nio.file 是 Java 7 引入的标准库,安卓并未完全兼容这部分 API(尤其是旧版本安卓系统),因此调用 Paths.get() 会因 “方法参数不匹配” 或 “API 不存在” 而报错。
怎么办呢?有两种方法来实现懒人精灵的文件复制。
一种是使用lua的io文件操作方法,另一种是使用go来实现文件复制。
lua实现文件复制
function copyFileByLua(原始路径,复制路径)
local inputFile = io.open(原始路径, "rb")
if inputFile then
local outputFile = io.open(复制路径, "wb")
if outputFile then
local content = inputFile:read("*a")
outputFile:write(content)
inputFile:close()
outputFile:close()
return true
end
inputFile:close()
end
return false
end
go实现文件复制
import (
"fmt" // 新增
"io" // 新增
"os" // 新增
)
// CopyFile 复制文件,适用于大文件
// src: 源文件路径
// dst: 目标文件路径
// 返回: 复制是否成功 (true: 成功, false: 失败)
func CopyFile(src, dst string) bool {
// 打开源文件(以只读模式打开源文件)
sourceFile, err := os.Open(src)
if err != nil {
bridge.PrintLog(fmt.Sprintf("Error: 无法打开源文件 %s: %v", src, err))
return false
}
defer sourceFile.Close()
// 创建目标文件,如果已存在则覆盖(如果文件已存在,它会被截断(清空)。)
destFile, err := os.Create(dst)
if err != nil {
bridge.PrintLog(fmt.Sprintf("Error: 无法创建目标文件 %s: %v", dst, err))
return false
}
defer destFile.Close()
// 使用 io.Copy 进行高效的文件复制(在 Go 中,处理大文件复制时,最高效且内存友好的方式是使用 io.Copy,它会自动处理缓冲,避免一次性将整个文件读入内存。)
_, err = io.Copy(destFile, sourceFile) // 核心步骤,它从源文件读取内容并写入目标文件,内部实现了高效的缓冲机制。
if err != nil {
bridge.PrintLog(fmt.Sprintf("Error: 复制文件内容时出错: %v", err))
return false
}
// 确保所有内容都写入磁盘(它确保所有内存中的缓冲数据都已物理写入到存储设备上,防止数据在程序崩溃时丢失。)
err = destFile.Sync()
if err != nil {
bridge.PrintLog(fmt.Sprintf("Error: 同步文件到磁盘时出错: %v", err))
return false
}
return true
}
在 Go 中,处理大文件复制时,最高效且内存友好的方式是使用 io.Copy,它会自动处理缓冲,避免一次性将整个文件读入内存。
代码解释
os.Open(src): 以只读模式打开源文件。如果文件不存在或没有权限,会返回错误。os.Create(dst): 创建目标文件。如果文件已存在,其内容会被清空。如果目录不存在,会返回错误。defer ...Close(): 使用defer确保在函数退出前,无论成功还是失败,打开的文件句柄都会被关闭,防止资源泄漏。io.Copy(destFile, sourceFile): 这是复制操作的核心。它从sourceFile读取数据并写入destFile,内部使用了一个 32KB 的缓冲区,非常适合处理大文件,因为它不会耗尽内存。destFile.Sync(): 这是一个重要的步骤。它确保所有内存中的缓冲数据都已物理写入到存储设备上,防止数据在程序崩溃时丢失。- 错误处理: 函数在每一步都检查了错误,并使用
fmt.Errorf创建了包含上下文信息(如文件名)的错误消息,这使得调试更加容易。 - 注册: 通过
bridge.Register,Go 函数CopyFile被暴露给 Lua 环境。在 Lua 中,你可以通过gobridge.call('libgo.so', 'CopyFile', srcPath, dstPath)来调用它。
go实现文件移动
在 Go 中,移动文件最直接的方式是使用 os.Rename。然而,os.Rename 有一个重要的限制:它不能跨文件系统或磁盘分区移动文件。
为了创建一个健壮的、可以在任何情况下工作的 MoveFile 方法,我们应该采用“先复制,后删除”的策略。
这与我们之前实现的 Files.move() 在 Java 中的行为类似,能确保跨文件系统操作的成功。
// MoveFile 移动文件,支持跨文件系统
// src: 源文件路径
// dst: 目标文件路径
// 返回: 移动是否成功 (true: 成功, false: 失败)
func MoveFile(src, dst string) bool {
// 首先尝试使用 os.Rename,这是最高效的方式(在同一个文件系统内)
err := os.Rename(src, dst)
if err == nil {
// Rename 成功,直接返回
return true
}
// Rename 失败,很可能是因为跨文件系统,采用“复制-删除”策略
bridge.PrintLog(fmt.Sprintf("Rename failed (%v), attempting copy-and-delete.", err))
// 1. 复制文件
if !CopyFile(src, dst) {
// 复制失败,整个移动操作失败
return false
}
// 2. 删除源文件
err = os.Remove(src)
if err != nil {
// 复制成功但删除失败,这是一个需要关注的警告
bridge.PrintLog(fmt.Sprintf("Warning: successfully copied file to %s, but failed to remove original file %s: %v", dst, src, err))
// 根据需求,这里可以返回 true (因为核心内容已移动) 或 false (因为操作不完整)
// 通常认为移动成功,因为目标文件已存在
return true
}
// 复制和删除都成功
return true
}
代码解释
os.Rename(src, dst): 这是 Go 标准库中移动文件(或在同分区重命名)的系统调用。它非常快,因为通常只是修改文件系统的元数据。我们首先尝试它,因为它是最优解。- 错误处理与回退:如果
os.Rename返回一个错误(err != nil),我们打印一条日志说明将要尝试备用方案。然后调用CopyFile。 CopyFile(src, dst): 我们复用了刚刚创建的CopyFile函数。如果它返回false,意味着复制失败,那么整个移动操作也必须失败,所以我们return false。os.Remove(src): 只有在复制成功后,我们才尝试删除源文件。- 删除失败的警告:如果
os.Remove失败,情况比较特殊。文件已经被成功复制到目标位置,但原始文件未能删除。这通常是一个权限问题。我们记录一条Warning级别的日志。在大多数自动化场景中,这可以被视为“成功”,因为最重要的数据已经安全到达目的地。因此,这里选择return true。如果你的业务场景要求必须删除源文件,那么应该改为return false。 - 注册: 将
MoveFile函数暴露给 Lua,使其可以被调用。
调用java的方法来实现文件移动
-- 将一条视频从工作目录搬运到相册
function 辅助_搬运视频_old()
local sourcePath = workPath .. _ENV['uiParam']['magicConfig']['video_type'] -- 视频文件存储路径(从HTML前端页面获取)
local targetPath = '/sdcard/DCIM/Camera/'
local workEditVideoInfo = getOneVideoFileName(sourcePath) -- 获取一条视频信息
local destPath = targetPath .. workEditVideoInfo['name'] -- 目标路径
-- print(workEditVideoInfo)
-- 调用java的方法移动文件
local source = File(workEditVideoInfo['path'])
local target = File(destPath)
print('源地址: ' .. workEditVideoInfo['path'])
print('目标地址: ' .. destPath)
if source.exists() then
if source.renameTo(target) then
-- 建议添加文件大小或内容的校验。
sleep(5*1000)
print('文件移动成功')
else
print('文件移动失败')
end
end
return target.exists() and target.length() > 0
end
1. 官方交流QQ群,添加多个不批。建议使用安卓手机或电脑申请。
飞云脚本圈: 586333520
Auto.js学习交流③群:286635606
Auto.js学习交流②群:712194666(满员)
IOS免越狱自动化测试群:691997586
2. 盗版,破解有损他人权益和违法作为,请各位会员支持正版。
3. 本站部分资源来源于用户上传和网络搜集,如有侵权请提供版权证明并联系站长删除。
4.如未特别申明,本站的技术性文章均为原创,未经授权,禁止转载/搬运等侵权行为。
5.全站所有付费服务均为虚拟商品,购买后自动发货。售出后概不接受任何理由的退、换。注册即为接受此条款。
6.如果站内内容侵犯了您的权益,请联系站长删除。
飞云脚本 » 【懒人精灵】使用go来移动和复制文件
飞云脚本圈: 586333520

Auto.js学习交流③群:286635606
Auto.js学习交流②群:712194666(满员)
IOS免越狱自动化测试群:691997586
2. 盗版,破解有损他人权益和违法作为,请各位会员支持正版。
3. 本站部分资源来源于用户上传和网络搜集,如有侵权请提供版权证明并联系站长删除。
4.如未特别申明,本站的技术性文章均为原创,未经授权,禁止转载/搬运等侵权行为。
5.全站所有付费服务均为虚拟商品,购买后自动发货。售出后概不接受任何理由的退、换。注册即为接受此条款。
6.如果站内内容侵犯了您的权益,请联系站长删除。
飞云脚本 » 【懒人精灵】使用go来移动和复制文件