Lua - 通过 HTTP 异步下载文件

Lua - Download file asynchronously via HTTP

提问人:Luli 提问时间:4/13/2022 最后编辑:Luli 更新时间:4/14/2022 访问量:217

问:

我刚刚读完了核心代码。我想编写代码从网站异步下载文件,但 copas 似乎只支持套接字 IO。copas

由于 Lua 不提供异步语法,并且其他包肯定会有自己的事件循环,我认为这些循环不能沿着 copas 的循环运行。

那么要通过 http 异步下载文件,我是否必须找到一个同时支持异步 http 和异步文件 IO 的包?还是其他想法?

异步 Lua IO

评论

0赞 Luli 4/13/2022
我刚刚意识到在 copas 中恢复每个任务的函数实际上是导出的,因此可以使用这个函数、一个数据队列、一堆异步文件编写器作为消费者来制作异步下载器。stepstep

答:

0赞 Luli 4/14/2022 #1

在阅读了一堆代码之后,我终于可以回答我自己的问题了。

正如我在对这个问题的评论中提到的,可以利用异步 IO 库导出的 step 函数,并将多个步进合并到一个更大的循环中。

在 的情况下,它使用 C 语言中的外部线程池来管理文件 IO,并使用单线程循环来调用挂起的回调并管理 IO 轮询(在我的用例中不需要轮询)。luv

可以简单地调用 提供的文件操作函数来制作异步文件 IO。但仍然需要 step 的循环来调用回调绑定到 IO 操作。luvluv

整数主循环如下所示:

local function main_loop()
    copas.running = true
    while not copas.finished() or uv.loop_alive() do
        if not copas.finished() then
            copas.step()
        end
        if uv.loop_alive() then
            uv.run("nowait")
        end
    end
end

copas.step()是 的步进函数。并且只运行一次事件循环,并且在轮询时没有准备好的 IO 时不要阻塞。copasuv.run("nowait")luv

一个有效的解决方案如下所示:

local copas = require "copas"
local http = require "copas.http"
local uv = require "luv"

local urls = {
    "http://example.com",
    "http://example.com"
}

local function main_loop()
    copas.running = true
    while not copas.finished() or uv.loop_alive() do
        if not copas.finished() then
            copas.step()
        end
        if uv.loop_alive() then
            uv.run("nowait")
        end
    end
end

local function write_file(file_path, data)
    -- ** call to luv async file IO **
    uv.fs_open(file_path, "w+", 438, function(err, fd)
        assert(not err, err)
        uv.fs_write(fd, data, nil, function(err_o, _)
            assert(not err_o, err_o)
            uv.fs_close(fd, function(err_c)
                assert(not err_c, err_c)
                print("finished:", file_path)
            end)
        end)
    end)
end

local function dl_url(url)
    local content, _, _, _ = http.request(url)
    write_file("foo.txt", content)
end

-- adding task to copas' loop
for _, url in ipairs(urls) do
    copas.addthread(dl_url, url)
end

main_loop()