HTTP
一、HTTP 概述
此部分内容只是简单的对 HTTP 作一个介绍,更详细的说明或协议文档,请查阅相关网站或文档。
1.1 HTTP 请求方法
HTTP/1.1 协议中共定义了八种方法来以不同方式操作指定的资源。
a.GET
向指定的资源发出请求。使用 GET 方法应该只用在读取数据。
b.HEAD
与 GET 方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。
c.POST
向指定资源提交数据,请求服务器进行处理,例如上传文件。
d.PUT
向指定资源位置上传其最新内容。
e.DELETE
请求服务器删除 Request-URI 所标识的资源。
f.TRACE
回显服务器收到的请求,主要用于测试或诊断。
g.OPTIONS
这个方法可使服务器传回该资源所支持的所有 HTTP 请求方法。用’*'来代替资源名称,向 Web 服务器发送 OPTIONS 请求,可以测试服务器功能是否正常运作。
h.CONNECT
HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。通常用于 SSL 加密服务器的链接。
HTTP 服务器至少应该实现 GET 和 HEAD 方法,其他方法都是可选的。
1.2 HTTP 状态码
状态代码的第一个数字代表当前响应的类型:
1xx 消息——请求已被服务器接收,继续处理
2xx 成功——请求已成功被服务器接收、理解、并接受
3xx 重定向——需要后续操作才能完成这一请求
4xx 请求错误——请求含有词法错误或者无法被执行
5xx 服务器错误——服务器在处理某个正确请求时发生错误
RFC 2616 中已经推荐了描述状态的短语,例如"200 OK",“404 Not Found”。
1.3 URL 链接地址
超文本传输协议(HTTP)的地址包含五个基本元素,分别是:
a.传送协议, 层级 URL 标记符号(为[//],固定不变) 访问资源需要的凭证信息服务器,一般情况下为域名,也可以使用 IP 地址。
b.端口号,以数字方式表示,HTTP 默认为”:80“,默认值可不给出。
c.路径,字符“/”区别路径中的每一个目录名称。
d.查询,GET 模式的窗体参数,用“?”字符作为起点,每个参数用“&”隔开,再以“=”分开参数名称与数据。
e.片段,以“#”字符为起点。
由于超文本传输协议允许服务器将浏览器重定向到另一个网页地址,因此许多服务器允许用户省略网页地址中的部分内容,比如 www。
本文通过几个具体的例子,演示 http 与 https 协议的具体实现。
二、功能演示概述
2.1 演示概要
本文档使用 Air8101 开发板,就 Http 协议进行了较系统的演示,展示了 Air8101 在网络应用上的便捷性、严谨性以及简易性。使用者不需要有太多的 Http 协议相关的知识,也非常的容易上手,并完成特定的任务。本文档除了对 GET、POST 方法进行最基本的演示外,还在上传文件、下载文件以及 gzip 应用等方面作了一些扩展,具有一定的深度与广度,既可适于初学者,也可供有相当基础的开发人员参考。
2.2 Air8101 模块简介
Air8101 以 WiFi6 低功耗音视频 SoC 芯片为核心,针对物联网量身定制,具有功耗低、集成度高、安全性能高、接口与资源丰富等优势,同时集成音频、视频解码接口,是中控显示、智能门锁、远程监控等应用的不二选择。
2.3 网络环境要求
本演示是测试 “http” 协议,因而需要在演示时有网络,Ari8101 开发板是基于 wifi 的网络连接,因而外部环境要求有 wifi 网络,并在输入 SSID 及 密码后可直接连接上 “Internet”,不能再有须要转网页输密码等环节。可以使用手机热点进行连接并测试。
2.4 硬件环境及相关辅件
演示基于 Ari8101 开发板,串行口通讯模块一个以及用于运行软件 Luatools 的 PC 机及 USB 连接线一条。
三、平台搭建
3.1、硬件平台的搭建
[!TIP] 待模块开发板出来后更换图片 本演示使用 Air8101 开发板作为硬件平台,如图所示:
[!TIP] (相关内容有待正式开发板出来后,按正式板资源再组织语言)
通过开发板的通讯模块,接到 DL_UART0 的端口,如上图,将跳线子按图中所示配置好,接上 USB 口与 PC 机的 USB 口连接线,此时 PC 设备管理器将出新出现一个串行口设备,记住其端口号备用。
串行口的驱动程序请从此 合宙云盘目录 下载即可。
3.2、Luatools 软件准备
Luatools 最新版本可从网站 Luatools 下载和使用教程 - 合宙模组资料中心 下载,这里也有官方的软件使用说明书,请大家仔细研读,以熟练使用本工具。
Luatools 工具安装完成后,起动 Luatools 工具软件,如下图所示:
点击"下载固件"按钮,选择最新固件,本文档编写时的最新固件在下面给出了下载链接,大家可以直接取用。
如下图,选择好了固件,点击下载,将固件下载到开发板。
下载成功后,则本文档所要求的演示运行环境,即已经搭建完成。也就是说现在开发板具备了运行 lua 脚本的基本要求。特别注意的就是,固件脚本下载一次即可,不需要每次都下载。只有固件有更新或因需要替换成不同版本的固件时,才需要重新下载。
3.3、项目建立
现在我们就来建立一个项目,并通过项目来管理各 lua 脚本文件,脚本文件编写完成,即可加入到项目下载到开发板运行并查看运行结果。譬如下图中,就有多个项目,各项目完成不同的功能。本文档所建立的项目名称为 air8101,项目仅包含一个文件 main.lua。
在 Lautools 软件界面中,点击“项目管理测试”按钮,进入项目管理界面,如图:
在上图中,点击“创建”按钮并输入项目名称创建一个新项目,输入 “air8101”,名称可以任取,各位依兴趣或实际要求定义即可。
项目创建后,在界面的右边,点击“增加脚本或资源文件”,并选择 main.lua 文件,将测试文件加入到项目中。main.lua 文件列出于第一章 DEMO 开发中,各位可以复制粘贴创建一个 main.lua 文件,同时各位也可以下载本文档的演示例程 main.lua 直接使用。点击下面的 main.lua 字样即可直接下载。
四、功能演示
4.1 总体部署与规划
本文档完成 GET、POST 方法的演示,并进而对基于此方法的上传下载文件,gzip 传输数据进行了扩展。main.lua 文件的基本格式如下面代码所示,最开始部分是项目名称及版本号,接下来要将本文件所要用到的模块包含进来,如_G.sys = require("sys")。这两部分各项目的 main.lua 基本都类似。
然后就是具体的功能或者项目需求编写,本文档为了完成 Http 的各需求,规划了两个协程(或者称任务),即网络任务与具体的功能演示任务,如下面代码所示。我们的演示将以此代码为基础,每完成一项功能,就增加一个函数,并在功能演示任务中增加一行调用,为了简明起见,也为了观察演示结果,当演示一项功能时,可以将其它功能演示注释掉,即在功能任务中将该功能的函数调用注释掉即可。完整的源代码 main.lua 将在本文档的最后提供下载。
-- LuaTools需要PROJECT和VERSION这两个信息
--必须在这个位置定义PROJECT和VERSION变量
--PROJECT:ascii string类型,可以随便定义,只要不使用,就行
--VERSION:ascii string类型,如果使用Luat物联云平台固件升级的功能,必须按照"X.X.X"定义,
--X表示1位数字;否则可随便定义
PROJECT = "httpdemo"
VERSION = "1.0.0"
--[[
本demo需要http库, 大部分能联网的设备都具有这个库
http也是内置库, 无需require
如需上传大文件,请使用 httpplus 库, 对应demo/httpplus
]]
-- sys库是标配
_G.sys = require("sys")
--[[特别注意, 使用http库需要下列语句]]
_G.sysplus = require("sysplus")
-- 网络任务
sys.taskInit(function()
local result,data
-----------------------------
-- 统一联网函数, 可自行删减
----------------------------
if wlan and wlan.connect then
-- wifi 联网, ssid表示wifi网络名称,password为网络密码
local ssid = "ChinaNet-7jU2"
local password = "xnceqvkr"
--输出wifi的用户名称、密码及硬件库名称
log.info("wifi", ssid, password, rtos.bsp() )
-- LED = gpio.setup(12, 0, gpio.PULLUP)
-- 网络初始化
wlan.init()
-- 运行模式为 工作站/客户端
wlan.setMode(wlan.STATION)
end
while true do
if wlan and wlan.connect then
-- 连接网络
wlan.connect(ssid, password, 1)
-- 在网络连接成功时,会发布一个系统消息 IP_READY,而
-- sys.waitUntil 订阅此消息,能在设置的时间内收到此消息
-- 即表示网络连接成功。
result, data = sys.waitUntil("IP_READY", 30000)
log.info("wlan", "IP_READY", result, data)
-- 取得网络Mac
device_id = wlan.getMac()
else
while 1 do
sys.wait(1000)
log.info("http", "当前固件未包含http库")
end
end
if result == true then
log.info("已联网")
sys.publish("net_ready")
end
while wlan and wlan.ready() do
sys.wait(1000)
end
end
end)
-- 功能实现区
-- GET 请求
function demo_http_get()
-- 最普通的Http GET请求
local code, headers, body = http.request("GET", "https://www.air32.cn/").wait()
log.info("http.get", code, headers, body)
sys.wait(100)
local code, headers, body = http.request("GET", "https://www.luatos.com/").wait()
log.info("http.get", code, headers, body)
-- 按需打印
-- code 响应值, 若大于等于 100 为服务器响应, 小于的均为错误代码
-- headers是个table, 一般作为调试数据存在
-- body是字符串. 注意lua的字符串是带长度的byte[]/char*, 是可以包含不可见字符的
-- log.info("http", code, json.encode(headers or {}), #body > 512 and #body or body)
end
-- 功能演示任务
sys.taskInit(function()
sys.wait(100)
-- 打印一下支持的加密套件, 通常来说, 固件已包含常见的99%的加密套件
-- if crypto.cipher_suites then
-- log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
-- end
-------------------------------------
-------- HTTP 演示代码 --------------
-------------------------------------
sys.waitUntil("net_ready") -- 等联网
while 1 do
-- 大家可以在下面的演示函数中,选择需要进行操作的功能,去掉注释符,
-- 保存后下载到开发板进行测试演示
-- 演示GET请求
demo_http_get()
sys.wait(1000)
-- 打印一下内存状态
log.info("sys", rtos.meminfo("sys"))
log.info("lua", rtos.meminfo("lua"))
--sys.wait(600000)
sys.wait(10000)
while wlan and wlan.ready() == false do
log.info("http demo","wifi_disconnected")
sys.waitUntil("net_ready",10000) -- 等联网
end
end
end)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
4.2 GET 方法测试
我们以上一节所提供的基本框架文件为基础来进行各项测试,因上节所提供的基本框架已经包含了 GET 方法的功能函数 demo_http_get(),因而我们直接将上面代码下载到开发板即可。将上面代码保存为 main.lua,将此文件加入项目,即可点击“下载脚本”按键,将脚本下载到开发板中,下载完成后开发板将自动重启,并显示连网相关内容,如图:
在上图中,我们看到了网络连接的相关内容,如图中较小红框中所示内容,如 IP 地址是:192.168.1.4,使用网关作为 DNS 服务器等。
在较大的红框中,我所看到了最简单的 http 请求操作,即:
local code, headers, body = http.request("GET", "https://www.air32.cn/").wait()
log.info("http.get", code, headers, body)
这段代码使用 “GET” 方法,访问网站:“https://www.air32.cn”,而较大的红框中的内容正是访问时的响应。我们可以看到网站的应答代码 “200” 表示响应正确,同时我们也看到了由 “body” 到 “/body” 之间的网站响应内容。
这是一个最基本的测试,也是对硬件或者开发板的一个初步检验,经过这个测试,一方面表示硬件与软件工作都非常正常。
4.3 POST 方法测试
4.3.1 POST 方法中的数据类型
Ⅰ. form-data:
通常用于文件上传和表单提交。数据以键值对的形式发送,每个键值对可以包含文本(text)或文件(file)。适合上传文件时使用。
Ⅱ. x-www-form-urlencoded:
通常用于普通的表单提交(例如 HTML 表单,登录表单、搜索表单)。数据以 URL 编码的键值对形式发送。所有特殊字符都会被 URL 编码。
Ⅲ. raw:
发送纯文本数据。可以是 JSON、XML、HTML、文本等。用户可以完全控制数据的格式和内容。
Ⅳ. binary:
发送二进制数据,例如图片、文件等。直接发送文件的二进制内容,不进行任何编码或处理。
Ⅴ. GraphQL:
发送 GraphQL 查询。允许用户使用 GraphQL 查询语言发送请求。请求体通常包含一个查询字符串和可选的变量。
当然还有一些数据类型,不再一一列举,有兴趣的同仁可以查阅有关资料获取相关内容。本演示不涉及过多的内容,仅演示 raw 的 Json 文本数据类型, x-www-form-urlencoded 表单数据类型,至于 form-data 数据类型在文件上传这部分的内容将会涉及,因而这里也不再另开小节专门讲述。
4.3.2 POST 方法 Json 数据内容的演示
本节内容使用 HTTP - luatos@air724ug - 合宙文档中心 文档同样的测试源,完成与该文档 POST 方法同样的演示与测试。大家可以阅读该文档的第七章《POST 请求演示》来了解该接口的有关事项,本文不再赘述,本文直接给出演示代码并展示演示结果。
演示代码如下:
function demo_http_post_json()
-- POST request 演示
local req_headers = {}
req_headers={
["Authorization"] = "Basic NkZhbXFsRmZTVmQ4OHNHejpLemt0SW8yUTNXcFhmbXRJTEtqME1mc3dsbHF0cTV0aldQM1BPUFU2d1M2M0E5VVlYOHJ1SFZCSVRaejlBak5w",
["Content-Type"] = "application/json",
["Connection"] = "keep-alive"
}
local body = json.encode(
{
["query_date"] = "20241212",
["iccids"] = "89860403102080512138"
}
)
local code, headers, body = http.request("POST","http://api.taoyuan-tech.com/api/open/iotcard/usagelog",
req_headers,
body -- POST请求所需要的body, string, zbuff, file均可
).wait()
log.info("http.post", code, headers, body)
end
上面代码为一个函数,具体就是完成 Json 的数据内容的演示。可将此函数加入到前面的 DEMO 代码 main.lua 中,然后在 taskInit 所创建的任务中运行,具体如下:
sys.taskInit(function()
sys.wait(100)
-------------------------------------
-------- HTTP 演示代码 --------------
-------------------------------------
sys.waitUntil("net_ready") -- 等联网
while 1 do
-- 演示GET请求
--demo_http_get()
-- POST一个json字符串
demo_http_post_json()
sys.wait(1000)
-- 打印一下内存状态
log.info("sys", rtos.meminfo("sys"))
log.info("lua", rtos.meminfo("lua"))
sys.wait(600000)
end
end)
完成 main.lua 的修订并保存后,在 Luatools 下载脚本到开发板,然后等待开发板重启,即可查看到代码运行结果,如下图所示:
在上图中红色框中的内容即是查询内容,返回了所查询卡片的当前状态。
4.3.3 POST 方法 x-www-form-urlencoded 数据内容的演示
与前一节类似,使用一函数来完成此演示要求(本文如无特别说明,都使用此种方式,一个函数完成一个演示,后面不再作说明),函数代码如下:
function demo_http_post_form()
-- POST request 演示
local req_headers = {}
req_headers["Content-Type"] = "application/x-www-form-urlencoded"
local params = {
ABC = "123",
DEF = "345"
}
local body = ""
for k, v in pairs(params) do
body = body .. tostring(k) .. "=" .. tostring(v):urlEncode() .. "&"
end
local code, headers, body = http.request("POST","http://httpbin.air32.cn/post",
req_headers,
body -- POST请求所需要的body, string, zbuff, file均可
).wait()
log.info("http.post.form", code, headers, body)
end
这是一个回环服务器,即请求什么返回什么,因而参数可以随意编写,符合格式要求即可。将上面代码修订完成并保存后,通过 Luatools 下载到开发板,等待开发板重启并运行,如果如下图。
上图中,红色框中为运行结果,可以看到返回码为 200,即表示正确返回,返回数据在 form 表格中,即:
form{
"ABD":"123"
"DEF":"345"
}
有关的头部信息牌 headers 表格中,不再列举。由上在返回数据可知,返回内容与发送内容相符。
4.4 Http 文件下载
文件下载可以通过 GET 方法实现,也可以通过 POST 方法实现,大家可以参考 HTTP - luatos@air724ug - 合宙文档中心 第九章的内容,本文使用 GET 方法与 POST 方法分别演示文件下载。
4.4.1 GET 方法文件下载
也没有什么好说的,直接上代码吧。
function demo_http_download()
--http.request("GET","http://zuoye.free.fr/files/flag.png",nil,nil,nil,nil,cbFnc)
--关于http文件下载的演示,由于服务器比较难找,因而使用了http://zuoye.free.fr网站,如果下面的文件
--演示失败,请试着打开上面的链接,查看文件是否存在,或者更换文件测试。如果该网站不存在了,那还请大
--家谅解一二。或者大家也可以使用自己知道的网站资源来对http的下载功能进行测试。
local code, headers, body = http.request("GET","http://zuoye.free.fr/files/flag.png",
{}, -- 请求所添加的 headers, 可以是nil
"",
nil
).wait()
log.info("http.get", code, headers, string.toHex(body)) -- 只返回code和headers
end
修订完成后保存,并下载到开发板中,等待开发板重启运行,运行结果如下:
上图是演示结果返回,由于 png 文件为二进制数据形式,因而通过 toHex 函数转化化十六进制文本格式显示,如图中红色框中内容,演示返回码为 200 表示演示正确返回。
4.4.2 POST 方法文件下载
为了与前面 GET 方法文件下载区别,此函数命名为 demo_http_download_post,以示区别。
function demo_http_download_post()
-- POST and download, task内的同步操作
local opts = {} -- 额外的配置项
opts["dst"] = "/data.bin" -- 下载路径,可选
opts["timeout"] = 30000 -- 超时时长,单位ms,可选
-- opts["adapter"] = socket.ETH0 -- 使用哪个网卡,可选
-- opts["callback"] = http_download_callback
-- opts["userdata"] = http_userdata
for k, v in pairs(opts) do
print("opts",k,v)
end
local code, headers, body = http.request("POST","http://site0.cn/api/httptest/simple/date",
{}, -- 请求所添加的 headers, 可以是nil
"",
opts
).wait()
log.info("http.post", "code ="..code.." headers = ",headers, " body= "..body) -- 只返回code和headers
--大家也可以依具体情况对文件进行操作处理
local f = io.open("/data.bin", "rb")
if f then
local data = f:read("*a")
log.info("fs", "data", data, data:toHex())
end
-- GET request, 开个task让它自行执行去吧, 不管执行结果了
sys.taskInit(http.request("GET","http://site0.cn/api/httptest/simple/time").wait)
end
上面这段代码中,除了通过 http 协议将文件下载的演示,还演示了如何进行文件操作,具体操作结果如下图所示:
由上图中的反馈结果可以看到返回码 200,表示操作成功。
我们也可以看到文件操作的结果,打开文件读取的数据是“Thu Dec 26 14:13:01 2024”,其后就是转化成为 HEX 格式的字符“546875204465632032362031343A31323A353020323032340”。
4.5 Http 文件上传
上文说到有关数据类型时,说到 form-data 的演示在文件上传中有演示,因而本节内容即是文件上传的演示,也是 form-data 的一个演示。
4.5.1 通用文件上传
阅读本文档时,可以参考 HTTP - luatos@air724ug - 合宙文档中心 第八章的相关内容。
---- MultipartForm上传文件
-- url string 请求URL地址
-- req_headers table 请求头
-- params table 需要传输的数据参数
function postMultipartFormData(url, params)
local boundary = "----WebKitFormBoundary"..os.time()
local req_headers = {
["Content-Type"] = "multipart/form-data; boundary="..boundary,
}
local body = {}
--log.info("postMultipart_params=",url.."----"..params.."------")
-- 解析拼接 body
for k,v in pairs(params) do
if k=="texts" then
local bodyText = ""
for kk,vv in pairs(v) do
print(kk,vv)
bodyText = bodyText.."--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"\r\n\r\n"..vv.."\r\n"
end
table.insert(body, bodyText)
elseif k=="files" then
local contentType =
{
txt = "text/plain", -- 文本
jpg = "image/jpeg", -- JPG 格式图片
jpeg = "image/jpeg", -- JPEG 格式图片
png = "image/png", -- PNG 格式图片
gif = "image/gif", -- GIF 格式图片
html = "image/html", -- HTML
json = "application/json" -- JSON
}
for kk,vv in pairs(v) do
if type(vv) == "table" then
for i=1, #vv do
print(kk,vv[i])
table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"; filename=\""..vv[i]:match("[^%/]+%w$").."\"\r\nContent-Type: "..contentType[vv[i]:match("%.(%w+)$")].."\r\n\r\n")
table.insert(body, io.readFile(vv[i]))
table.insert(body, "\r\n")
end
else
print(kk,vv)
table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"; filename=\""..vv:match("[^%/]+%w$").."\"\r\nContent-Type: "..contentType[vv:match("%.(%w+)$")].."\r\n\r\n")
table.insert(body, io.readFile(vv))
table.insert(body, "\r\n")
end
end
end
end
table.insert(body, "--"..boundary.."--\r\n")
body = table.concat(body)
log.info("headers: ", "\r\n" .. json.encode(req_headers), type(body))
log.info("body: " .. body:len() .. "\r\n" .. body)
local code, headers, body = http.request("POST",url,
req_headers,
body
).wait()
log.info("http.post", code, headers, body)
end
上面代码根据传入的参数 params 组织链接参数,并最终形成 http.request 函数的 body 参数,从而完成文件的上传操作。
分析上面的代码,我们发现上传文件的 body 参数的一般格式(传数据时类似,请大家自己理解),即由 boundary 作为边界所包括的内容,其中包括文件名、文件格式等内容,如下示例文本所示:
local body = "--"..boundary.."\r\n"..
"Content-Disposition: form-data; name=\"uploadFile\"; filename=\"luatos_uploadFile_TEST01.txt\""..
"\r\nContent-Type: text/plain\r\n\r\n"..
"1111http_测试一二三四654zacc\r\n"..
"--"..boundary
上面文档,boundary 就是边界,头尾都有,这个可以依个人喜好自行定义。后面 Content-Disposition 指明回复的数据类型,参数名称即 name 是 uploadFile,文件名 filename 为 luatos_uploadFile_TEST01.txt,文件内容的数据类型为 text/plain,其它内容就是文件的实际数据,即字符 “http_测试一二三四 654zacc\r\n”。
通过上面的分析,我们对上传文件的具体格式有了一个较清晰的了解,接下来我们在任务中调用此函数,如下面代码所示,在任务中添加调用代码:
sys.taskInit(function()
-------------------------------------
-------- HTTP 演示代码 --------------
-------------------------------------
sys.waitUntil("net_ready") -- 等联网
while 1 do
-- 演示GET请求
--demo_http_get()
-- 表单提交
-- demo_http_post_form()
-- POST一个json字符串
-- demo_http_post_json()
-- 上传文件, mulitform形式
--demo_http_post_file()
postMultipartFormData(
"http://airtest.openluat.com:2900/uploadFileToStatic",
{
-- texts =
-- {
-- ["imei"] = "862991234567890",
-- ["time"] = "20180802180345"
-- },
files =
{
["uploadFile"] = "/luadb/luatos_uploadFile.txt",
}
}
)
-- 文件下载
-- demo_http_download_old()
--demo_http_download()
-- gzip压缩的响应, 以和风天气为例
-- demo_http_get_gzip()
sys.wait(1000)
-- 打印一下内存状态
log.info("sys", rtos.meminfo("sys"))
log.info("lua", rtos.meminfo("lua"))
sys.wait(600000)
end
end)
保存上面代码,并依据上面的代码,我们新建一个文本文件,命名为 luatos_uploadFile.txt, 放到目录 luadb 下。编辑此文本文本,录入以下内容:
1 208
2 416
3 624
4 832
5 1040
6.84S + 5ms 开始
11100000 6.87S 00000111
00111010 6.9S 01011100
6.91S
11111000 6.93 00011111
6.94
01101100 6.96 00110110
6*2
*255
上面内容为随机输入,内容没有实际含义,请大家知悉。然后在 Luatools 工具内添加此文件,如下图所示:
完成这些操作下,点击“下载脚本”按钮将脚本及文件下载到开发板中,我们便可以等待开发重启并运行演示代码:果:
上图中,可以看到文件上传成功,大家可以仔细查看较大红框中的内容,是不是与上面所分析的格式相符。至于下面的小红框,则是表示成功的代码 200。
4.5.2 虚拟内存文件上传
通过上面的文件上传演示,有人就要问了,这个都需要事先准备文件,但有些文件可能是依据程序的运行动态产生的,比如日志,那这样的文件又要如何上传呢?
我们上面分析了上传文件的具体格式,那我们就可以通过拼接的方式,按格式组织 http.request 函数的 body 参数,从而达到上面类似日志文件的上传目的。
为此我们编写了下面的代码,请大家参考:
function demo_http_post_file()
-- -- POST multipart/form-data模式 上传文件---手动拼接虚拟内存文件
local boundary = "----WebKitFormBoundary"..os.time()
local req_headers = {
["Content-Type"] = "multipart/form-data; boundary="..boundary,
}
local body = "--"..boundary.."\r\n"..
"Content-Disposition: form-data; name=\"uploadFile\"; filename=\"luatos_uploadFile_TEST01.txt\""..
"\r\nContent-Type: text/plain\r\n\r\n"..
"1111http_测试一二三四654zacc\r\n"..
"--"..boundary
log.info("headers: ", "\r\n"..json.encode(req_headers))
log.info("body: ", "\r\n"..body)
local code, headers, body = http.request("POST","http://airtest.openluat.com:2900/uploadFileToStatic",
req_headers,
body -- POST请求所需要的body, string, zbuff, file均可
).wait()
log.info("http.post", code, headers, body)
end
这段代码我们就不作分析了,大家可以参考 3.6.1 节关于上传文件格式的有关内容理解上面代码。保存修改并将 main.lua 文件下载到开发板运行:
上图中,我们看到文件的具体内容,并看到了请求的返回代码 200,因而内存文件的上传亦圆满完成。
4.6 压缩 gzip 演示
Http 传输内容大多都经过了 gzip 压缩处理,因而解压缩是一个常规的操作,LuatOS 包含了一个简单的压缩库,即 miniz,大家可以参考 miniz - 简易 zlib 压缩 - LuatOS 文档 作更进一步的了解。我们以天气预报的数据包作为演示例子。首先我们可以直接通过浏览器看看得到的数据包会有哪些数据,然后我们再看具体代码可能就更容易理解。我们将链接 https://devapi.qweather.com/v7/weather/now?location=101010100&key=0e8c72015e2b4a1dbff1688ad54053de 输入到浏览器的地址栏内回车,得到如下所示的 Json 代码:
"code": "200",
"updateTime": "2024-12-20T22:17+08:00",
"fxLink": "https://www.qweather.com/weather/beijing-101010100.html",
"now": {
"obsTime": "2024-12-20T22:09+08:00",
"temp": "-2",
"feelsLike": "-11",
"icon": "150",
"text": "晴",
"wind360": "0",
"windDir": "北风",
"windScale": "5",
"windSpeed": "37",
"humidity": "28",
"precip": "0.0",
"pressure": "1026",
"vis": "25",
"cloud": "91",
"dew": "-17"
},
"refer": {
"sources": [
"QWeather"
],
"license": [
"CC BY-SA 4.0"
]
}
}
因为浏览器会自动进行解压缩,因而我们可以直接看到具体的数据,如上面文本所示。有了上面的数据,我们分析下面的代码就方便多了:
local function demo_http_get_gzip()
-- 这里用 和风天气 的API做演示
-- 这个API的响应, 总会gzip压缩过, 需要配合miniz库进行解压
local code, headers, body = http.request("GET", "https://devapi.qweather.com/v7/weather/now?location=101010100&key=0e8c72015e2b4a1dbff1688ad54053de").wait()
log.info("http.gzip", code)
if code == 200 then
local re = miniz.uncompress(body:sub(11), 0)
log.info("和风天气", re)
if re then
local jdata = json.decode(re)
log.info("jdata", jdata)
if jdata then
log.info("和风天气", jdata.code)
if jdata.now then
log.info("和风天气", "天气", jdata.now.text)
log.info("和风天气", "温度", jdata.now.temp)
end
end
end
end
end
这是一个很简单 GET 方法演示,代码也很简单,即取得返回的数据 body,然后取得 body 的从 11 位置开始的数据进行解压即可,然后就是依需要提取数据并输出。至于为何要提取从 11 位置开始的字符,我们从上面的结果代码可知,这个是返回的结果代码 “ code: 200 ”所占的空间,因而剔除在解压缩之外。
具体代码很简单就不作分析了,直接看看演示结果吧:
上面图中的红框分别是返回码 200、解压后的 Json 代码以及依需要选择的输出内容。
五、总结
本次拿到 Ari8101 开发板,感觉接口丰富,各模块都比较实用。用了几天时间进行了解,然后就使用这款开发板进行 http 的相关测试,感觉还是非常容易上手。结合以前关于 http 的文档 HTTP - luatos@air724ug - 合宙文档中心 顺利地完成了 GET、POST、GZIP 以及文件上传下载等操作,个人觉得收获很多。同时,也希望本文能给广大读者以帮助,作为参考解决一些实际的问题,使大家在使用 Air8101 时少走弯路,少花时间与精力。
六、 完全代码
最新固件:
固件如更新,一般也会同步更新 Luatools 工具包,因而具体使用时,当有 Luatools 更新时,及时更新即可。