【懒人精灵】使用go来移动和复制文件

前言

在写一个复制和移动视频文件的项目中,遇到了要复制的问题,但是懒人精灵并没有提供移动和复制文件的方法。需要自己去实现。

先尝试了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,它会自动处理缓冲,避免一次性将整个文件读入内存。

代码解释

  1. os.Open(src): 以只读模式打开源文件。如果文件不存在或没有权限,会返回错误。
  2. os.Create(dst): 创建目标文件。如果文件已存在,其内容会被清空。如果目录不存在,会返回错误。
  3. defer ...Close(): 使用 defer 确保在函数退出前,无论成功还是失败,打开的文件句柄都会被关闭,防止资源泄漏。
  4. io.Copy(destFile, sourceFile): 这是复制操作的核心。它从 sourceFile 读取数据并写入 destFile,内部使用了一个 32KB 的缓冲区,非常适合处理大文件,因为它不会耗尽内存。
  5. destFile.Sync(): 这是一个重要的步骤。它确保所有内存中的缓冲数据都已物理写入到存储设备上,防止数据在程序崩溃时丢失。
  6. 错误处理: 函数在每一步都检查了错误,并使用 fmt.Errorf 创建了包含上下文信息(如文件名)的错误消息,这使得调试更加容易。
  7. 注册: 通过 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
}

代码解释

  1. os.Rename(src, dst): 这是 Go 标准库中移动文件(或在同分区重命名)的系统调用。它非常快,因为通常只是修改文件系统的元数据。我们首先尝试它,因为它是最优解。
  2. 错误处理与回退:如果 os.Rename 返回一个错误(err != nil),我们打印一条日志说明将要尝试备用方案。然后调用 CopyFile
  3. CopyFile(src, dst): 我们复用了刚刚创建的 CopyFile 函数。如果它返回 false,意味着复制失败,那么整个移动操作也必须失败,所以我们 return false
  4. os.Remove(src): 只有在复制成功后,我们才尝试删除源文件。
  5. 删除失败的警告:如果 os.Remove 失败,情况比较特殊。文件已经被成功复制到目标位置,但原始文件未能删除。这通常是一个权限问题。我们记录一条 Warning 级别的日志。在大多数自动化场景中,这可以被视为“成功”,因为最重要的数据已经安全到达目的地。因此,这里选择 return true。如果你的业务场景要求必须删除源文件,那么应该改为 return false
  6. 注册: 将 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来移动和复制文件

企业级大数据智能营销管理系统

源码转让