首页 > 其他分享 >Go + AideLua 实现云端布局

Go + AideLua 实现云端布局

时间:2024-05-27 18:33:10浏览次数:18  
标签:end name .. AideLua import Go path local 云端

需求

最近有AndroidLua联动的需求,但考虑到安全性想采用Go实现云端布局分发,Lua获取到本地再进行解压刷新。

思路

后端: Golang
安卓端: Java + Lua


  1. 云端编写代码,Go-server 实时进行分发
  2. 安卓端定时监听云端的文件变化, 获取到本地后解压并装载.
  3. 通过 SQLite等本地数据库,实现持久化存储云端布局和相关配置.
  4. 后续拓展可以使用 Java + Lua重新打包apk包

代码实现

  1. 云更
  2. 安卓打包
  3. 自定义请求库

Go 云端更新方案

go_server.go

package main

import (
	"archive/zip"
	"bytes"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
)

var (
	mu           sync.Mutex
	zipBuffer    *bytes.Buffer
	lastAccess   time.Time
	accessPeriod = 10 * time.Second
)

func main() {
	r := gin.Default()

	r.GET("/api/v1/file", func(c *gin.Context) {
		mu.Lock()
		defer mu.Unlock()

		if time.Since(lastAccess) < accessPeriod {
			c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "暂时没有"})
			return
		}

		lastAccess = time.Now()

		if zipBuffer == nil {
			c.JSON(http.StatusNoContent, gin.H{"message": "No file available"})
			return
		}

		c.Header("Content-Type", "application/zip")
		c.Header("Content-Disposition", "attachment; filename=demo.zip")
		c.Data(http.StatusOK, "application/zip", zipBuffer.Bytes())
	})

	go func() {
		for {
			packFiles()
			time.Sleep(10 * time.Second)
		}
	}()

	if err := r.Run(":9090"); err != nil {
		log.Fatal(err)
	}
}

func packFiles() {
	mu.Lock()
	defer mu.Unlock()

	// 检查目录下是否有文件变更
	files, err := os.ReadDir("res") // 替换为你的目录路径
	if err != nil {
		log.Println("Failed to read directory:", err)
		return
	}

	if len(files) == 0 {
		log.Println("No files available in the directory")
		return
	}

	// 创建一个内存缓冲区来存储zip文件
	buf := new(bytes.Buffer)
	zipWriter := zip.NewWriter(buf)

	// 遍历目录下的文件,并将它们添加到zip文件中
	for _, file := range files {
		if file.IsDir() {
			continue
		}

		filePath := filepath.Join("res", file.Name()) // 替换为你的目录路径
		data, err := os.ReadFile(filePath)
		if err != nil {
			log.Println("Failed to read file:", file.Name(), err)
			continue
		}

		fileWriter, err := zipWriter.Create(file.Name())
		if err != nil {
			log.Println("Failed to create zip file entry:", file.Name(), err)
			continue
		}

		_, err = fileWriter.Write(data)
		if err != nil {
			log.Println("Failed to write to zip file:", file.Name(), err)
		}
	}

	err = zipWriter.Close()
	if err != nil {
		log.Println("Failed to close zip writer:", err)
		return
	}

	// 将zip文件存储到缓存中
	zipBuffer = buf

	fmt.Println("Files packed into zip successfully")
}

Lua 安卓apk打包

bin.lua

require "lua.import"
import "java.util.zip.ZipOutputStream"
import "android.net.Uri"
import "java.io.File"
import "android.widget.Toast"
import "java.util.zip.CheckedInputStream"
import "java.io.FileInputStream"
import "android.content.Intent"
import "java.security.Signer"
import "java.util.ArrayList"
import "java.io.FileOutputStream"
import "java.io.BufferedOutputStream"
import "java.util.zip.ZipInputStream"
import "java.io.BufferedInputStream"
import "java.util.zip.ZipEntry"
import "android.app.ProgressDialog"
import "java.util.zip.CheckedOutputStream"
import "java.util.zip.Adler32"

local bin_dlg, error_dlg
local function update(s)
    bin_dlg.setMessage(s)
end

local function callback(s)
    LuaUtil.rmDir(File(activity.getLuaExtDir("bin/.temp")))
    bin_dlg.hide()
    bin_dlg.Message = ""
    if not s:find("成功") then
        error_dlg.Message = s
        error_dlg.show()
    end
end

local function create_bin_dlg()
    if bin_dlg then
        return
    end
    bin_dlg = ProgressDialog(activity);
    bin_dlg.setTitle("正在打包");
    bin_dlg.setMax(100);
end

local function create_error_dlg2()
    if error_dlg then
        return
    end
    error_dlg = AlertDialogBuilder(activity)
    error_dlg.Title = "出错"
    error_dlg.setPositiveButton("确定", nil)
end

local function binapk(luapath, apkpath)
    require "lua.import"
    import "console"
    compile "mao"
    compile "sign"
    import "java.util.zip.*"
    import "java.io.*"
    import "mao.res.*"
    import "apksigner.*"
    local b = byte[2 ^ 16]
    local function copy(input, output)
        LuaUtil.copyFile(input, output)
        input.close()
        --[[local l=input.read(b)
      while l>1 do
        output.write(b,0,l)
        l=input.read(b)
      end]]
    end

    local function copy2(input, output)
        LuaUtil.copyFile(input, output)
    end

    local temp = File(apkpath).getParentFile();
    if (not temp.exists()) then

        if (not temp.mkdirs()) then

            error("create file " .. temp.getName() .. " fail");
        end
    end


    local tmp = activity.getLuaPath("tmp.apk")
    local info = activity.getApplicationInfo()
    local ver = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionName
    local code = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode

    --local zip=ZipFile(info.publicSourceDir)
    local zipFile = File(info.publicSourceDir)
    local fis = FileInputStream(zipFile);
    --local checksum = CheckedInputStream(fis, Adler32());
    local zis = ZipInputStream(BufferedInputStream(fis));

    local fot = FileOutputStream(tmp)
    --local checksum2 = CheckedOutputStream(fot, Adler32());

    local out = ZipOutputStream(BufferedOutputStream(fot))
    local f = File(luapath)
    local errbuffer = {}
    local replace = {}
    local checked = {}
    local lualib = {}
    local md5s = {}
    local libs = File(activity.ApplicationInfo.nativeLibraryDir).list()
    libs = luajava.astable(libs)
    for k, v in ipairs(libs) do
        --libs[k]="lib/armeabi/"..libs[k]
        replace[v] = true
    end

    local mdp = activity.Application.MdDir
    local function getmodule(dir)
        local mds = File(activity.Application.MdDir .. dir).listFiles()
        mds = luajava.astable(mds)
        for k, v in ipairs(mds) do
            if mds[k].isDirectory() then
                getmodule(dir .. mds[k].Name .. "/")
            else
                mds[k] = "lua" .. dir .. mds[k].Name
                replace[mds[k]] = true
            end
        end
    end

    getmodule("/")

    local function checklib(path)
        if checked[path] then
            return
        end
        local cp, lp
        checked[path] = true
        local f = io.open(path)
        local s = f:read("*a")
        f:close()
        for m, n in s:gmatch("require *%(? *\"([%w_]+)%.?([%w_]*)") do
            cp = string.format("lib%s.so", m)
            if n ~= "" then
                lp = string.format("lua/%s/%s.lua", m, n)
                m = m .. '/' .. n
            else
                lp = string.format("lua/%s.lua", m)
            end
            if replace[cp] then
                replace[cp] = false
            end
            if replace[lp] then
                checklib(mdp .. "/" .. m .. ".lua")
                replace[lp] = false
                lualib[lp] = mdp .. "/" .. m .. ".lua"
            end
        end
        for m, n in s:gmatch("import *%(? *\"([%w_]+)%.?([%w_]*)") do
            cp = string.format("lib%s.so", m)
            if n ~= "" then
                lp = string.format("lua/%s/%s.lua", m, n)
                m = m .. '/' .. n
            else
                lp = string.format("lua/%s.lua", m)
            end
            if replace[cp] then
                replace[cp] = false
            end
            if replace[lp] then
                checklib(mdp .. "/" .. m .. ".lua")
                replace[lp] = false
                lualib[lp] = mdp .. "/" .. m .. ".lua"
            end
        end
    end

    replace["libluajava.so"] = false

    local function addDir(out, dir, f)
        local entry = ZipEntry("assets/" .. dir)
        out.putNextEntry(entry)
        local ls = f.listFiles()
        for n = 0, #ls - 1 do
            local name = ls[n].getName()
            if name==(".using") then
                checklib(luapath .. dir .. name)
            elseif name:find("%.apk$") or name:find("%.luac$") or name:find("^%.") then
            elseif name:find("%.lua$") then
                checklib(luapath .. dir .. name)
                local path, err = console.build(luapath .. dir .. name)
                if path then
                    if replace["assets/" .. dir .. name] then
                        table.insert(errbuffer, dir .. name .. "/.aly")
                    end
                    local entry = ZipEntry("assets/" .. dir .. name)
                    out.putNextEntry(entry)

                    replace["assets/" .. dir .. name] = true
                    copy(FileInputStream(File(path)), out)
                    table.insert(md5s, LuaUtil.getFileMD5(path))
                    os.remove(path)
                else
                    table.insert(errbuffer, err)
                end
            elseif name:find("%.aly$") then
                local path, err = console.build(luapath .. dir .. name)
                if path then
                    name = name:gsub("aly$", "lua")
                    if replace["assets/" .. dir .. name] then
                        table.insert(errbuffer, dir .. name .. "/.aly")
                    end
                    local entry = ZipEntry("assets/" .. dir .. name)
                    out.putNextEntry(entry)

                    replace["assets/" .. dir .. name] = true
                    copy(FileInputStream(File(path)), out)
                    table.insert(md5s, LuaUtil.getFileMD5(path))
                    os.remove(path)
                else
                    table.insert(errbuffer, err)
                end
            elseif ls[n].isDirectory() then
                addDir(out, dir .. name .. "/", ls[n])
            else
                local entry = ZipEntry("assets/" .. dir .. name)
                out.putNextEntry(entry)
                replace["assets/" .. dir .. name] = true
                copy(FileInputStream(ls[n]), out)
                table.insert(md5s, LuaUtil.getFileMD5(ls[n]))
            end
        end
    end


    this.update("正在编译...");
    if f.isDirectory() then
        require "permission"
        dofile(luapath .. "init.lua")
        if user_permission then
            for k, v in ipairs(user_permission) do
                user_permission[v] = true
            end
        end


        local ss, ee = pcall(addDir, out, "", f)
        if not ss then
            table.insert(errbuffer, ee)
        end
        --print(ee,dump(errbuffer),dump(replace))


        local wel = File(luapath .. "icon.png")
        if wel.exists() then
            local entry = ZipEntry("res/drawable/icon.png")
            out.putNextEntry(entry)
            replace["res/drawable/icon.png"] = true
            copy(FileInputStream(wel), out)
        end
        local wel = File(luapath .. "welcome.png")
        if wel.exists() then
            local entry = ZipEntry("res/drawable/welcome.png")
            out.putNextEntry(entry)
            replace["res/drawable/welcome.png"] = true
            copy(FileInputStream(wel), out)
        end
    else
        return "error"
    end

    --print(dump(lualib))
    for name, v in pairs(lualib) do
        local path, err = console.build(v)
        if path then
            local entry = ZipEntry(name)
            out.putNextEntry(entry)
            copy(FileInputStream(File(path)), out)
            table.insert(md5s, LuaUtil.getFileMD5(path))
            os.remove(path)
        else
            table.insert(errbuffer, err)
        end
    end

    function touint32(i)
        local code = string.format("%08x", i)
        local uint = {}
        for n in code:gmatch("..") do
            table.insert(uint, 1, string.char(tonumber(n, 16)))
        end
        return table.concat(uint)
    end

    this.update("正在打包...");
    local entry = zis.getNextEntry();
    while entry do
        local name = entry.getName()
        local lib = name:match("([^/]+%.so)$")
        if replace[name] then
        elseif lib and replace[lib] then
        elseif name:find("^assets/") then
        elseif name:find("^lua/") then
        elseif name:find("META%-INF") then
        elseif !name:find("%a") then
        else
            local entry = ZipEntry(name)
            out.putNextEntry(entry)
            if entry.getName() == "AndroidManifest.xml" then
                if path_pattern and #path_pattern > 1 then
                    path_pattern = ".*\\\\." .. path_pattern:match("%w+$")
                end
                local list = ArrayList()
                local xml = AXmlDecoder.read(list, zis)
                local req = {
                    [activity.getPackageName()] = packagename,
                    [info.nonLocalizedLabel] = appname,
                    [ver] = appver,
                    [".*\\\\.lua"] = "",
                    [".*\\\\.luac"] = "",
                }
                if path_pattern==nil or path_pattern=="" then
                    req[".*\\\\.alp"] = ""
                    req["application/alp"] = "application/1234567890"
                  else
                    path_pattern=path_pattern:match("%w+$") or path_pattern
                    req[".*\\\\.alp"] = ".*\\\\."..path_pattern
                    req["application/alp"] = "application/"..path_pattern
                end
                for n = 0, list.size() - 1 do
                    local v = list.get(n)
                    if req[v] then
                        list.set(n, req[v])
                    elseif user_permission then
                        local p = v:match("%.permission%.([%w_]+)$")
                        if p and (not user_permission[p]) then
                            list.set(n, "android.permission.UNKNOWN")
                        end
                    end
                end
                local pt = activity.getLuaPath(".tmp")
                local fo = FileOutputStream(pt)
                xml.write(list, fo)
                local code = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode
                fo.close()
                local f = io.open(pt)
                local s = f:read("a")
                f:close()
                s = string.gsub(s, touint32(code), touint32(tointeger(appcode) or 1),1)
                s = string.gsub(s, touint32(18), touint32(tointeger(appsdk) or 18),1)

                local f = io.open(pt, "w")
                f:write(s)
                f:close()
                local fi = FileInputStream(pt)
                copy(fi, out)
                os.remove(pt)
            elseif not entry.isDirectory() then
                copy2(zis, out)
            end
        end
        entry = zis.getNextEntry()
    end
    out.setComment(table.concat(md5s))
    --print(table.concat(md5s,"/n"))
    zis.close();
    out.closeEntry()
    out.close()

    if #errbuffer == 0 then
        this.update("正在签名...");
        os.remove(apkpath)
        Signer.sign(tmp, apkpath)
        os.remove(tmp)
        activity.installApk(apkpath)
        --[[import "android.net.*"
        import "android.content.*"
        i = Intent(Intent.ACTION_VIEW);
        i.setDataAndType(activity.getUriForFile(File(apkpath)), "application/vnd.android.package-archive");
        i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        this.update("正在打开...");
        activity.startActivityForResult(i, 0);]]
        return "打包成功:" .. apkpath
    else
        os.remove(tmp)
        this.update("打包出错:\n " .. table.concat(errbuffer, "\n"));
        return "打包出错:\n " .. table.concat(errbuffer, "\n")
    end
end

--luabindir=activity.getLuaExtDir("bin")
--print(activity.getLuaExtPath("bin","a"))
local function bin(path)
    local p = {}
    local e, s = pcall(loadfile(path .. "init.lua", "bt", p))
    if e then
        create_error_dlg2()
        create_bin_dlg()
        bin_dlg.show()
        activity.newTask(binapk, update, callback).execute { path, activity.getLuaExtPath("bin", p.appname .. "_" .. p.appver .. ".apk") }
    else
        Toast.makeText(activity, "工程配置文件错误." .. s, Toast.LENGTH_SHORT).show()
    end
end

--bin(activity.getLuaExtDir("project/demo").."/")
return bin

自定义的Lua请求库

knet.lua

--
-- 更为便捷的请求封装
--

local kox = {
    _URL = nil,
    _DATA = nil,
    SomeThing = {},
    _COOKIES = nil,
    _METHOD = "get",
    _ChARSET = nil,
    _HEADERS = {
        ["User-Agent"] =
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69"
    }
}

---设置请求地址
---@param fetchUrl string
---@return table
function kox:setUrl(fetchUrl)
    if self._URL == nil then
        self._URL = fetchUrl
    end
    return self
end

--- 增加请求头
---@param key string
---@param value string
function kox:setHeaders(key, value, ...)
    local op = { ... }

    self._HEADERS[string.lower(key)] = value

    if #op > 0 then
        for k, v in pairs(op) do
            self._HEADERS[string.lower(tostring(k))] = v
        end
    end

    return self
end

--- 设置请求模式
---@param method string
function kox:setMethod(method)
    self._METHOD = string.lower(method)
    return self
end

--- 增加参数
---@param data string
function kox:setData(data)
    self._DATA = data
    return self
end

--- 发送请求
---@param callBack function
function kox:send(callBack, errorBack)
    xpcall(function()
        if self._METHOD == "get" then
            Http.get(self._URL, self._COOKIES, self._ChARSET, self._HEADERS, callBack)
        elseif self._METHOD == "post" then
            Http.post(self._URL, self._DATA, self._COOKIES, self._ChARSET, self._HEADERS, callBack)
        end
    end, errorBack)
    return self
end

-- 默认传递参数
function kox.Err(err)
    print(err)
end

return kox

--[[
Useage:
    local Request = import "kox.knet"
    Request:setUrl("https://www.baidu.com")
    Request:setMethod("Post")
    Request:setData(self.Params)
    Request:setHeaders("Content-Type", "application/x-www-form-urlencoded")
    Request:send(self.SuccessCaller, kox.Err)
]]

标签:end,name,..,AideLua,import,Go,path,local,云端
From: https://www.cnblogs.com/kongxiaoan/p/18216177

相关文章

  • mongodb查询平级数据,返回树形结构
    #mongodb查询数据,只返回一级、二级树形结构,子级数据对象整个返回db.t_ythgk_zd.aggregate([//1.匹配指定的dmlx{$match:{sjdm:"YTHGK_DIC_00002",yxx:1}},//2.查找上级代码(sjdm)对应的文档,并构建一个子文档数组{$lookup:{from:"t......
  • Sectigo SSL证书:全球SSL证书市场占有率最高品牌
    Sectigo(原ComodoCA)是全球SSL证书市场占有率最高的SSL证书品牌,由于其产品安全,价格低,受到大量用户的信任和欢迎。Sectigo旗下的SSL证书品牌包括Sectigo,PositiveSSL,SectigoEnterprise等。其SSL证书产品凭借卓越的安全性、广泛的认可度以及高效的服务,成为了众多企业和组......
  • 08Django项目--用户管理系统--查(前后端)
    对应视频链接点击直达@TOC一些朋友加我Q反馈,希望有每个阶段的完整项目代码,那从今天开始,我会上传完整的项目代码。用户管理,简而言之就是用户的增删改查。08项目点击下载,可直接运行(含数据库)链接:https://pan.baidu.com/s/1acb9eQt18ykxFO6TWKCRIw?pwd=1314提取码:1314查......
  • 翻译《The Old New Thing》- Hotkeys involving the Windows logo key are reserved b
    HotkeysinvolvingtheWindowslogokeyarereservedbythesystem-TheOldNewThing(microsoft.com)https://devblogs.microsoft.com/oldnewthing/20071130-00/?p=24333RaymondChen 2007年11月30日Windows徽标键的热键由系统保留        系统保留了......
  • 谷歌地图 | Google I/O '24 重磅发布助力企业拓展海外市场的新功能!
    编者按:本文是GoogleI/O2024系列的一部分,该系列分享了Google年度开发者大会上最新的GoogleMapsPlatform新闻。距全球首个GoogleMapsAPI问世已近20年。它引领了网络和移动端地理空间体验的革命。从那时起,GoogleMapsPlatform始终与开发者社区携手共进,不断发展,功能......
  • 关于全局变量的坑, golang熟手们经常遇到
    关于全局变量的坑,golang熟手们经常遇到原创 fengzi Go语言圈 2024-05-2708:30 广东 听全文Go语言圈Go语言开发者的学习好助手,分享Go语言知识,技术技巧,学习与交流Go语言开发经验,互动才有助于技术的提升,每天5分钟,助你GO语言技术快乐成长160篇原创内容公......
  • GO开发2024.x GoLand安装(亲测有效)
    GoLand为Go开发者打造的完整IDE。1.下载安装GoLand这里下载的是最新版本的2024.1.1,测试过2024最新版本没问题。2、可长期使用。下载地址:https://pan.baidu.com/s/1fgLjIzWR1-IJ1quhFdSCig?pwd=gwbtwindows系统Mac系统3、效果......
  • Go 基本数据类型
    Go基本数据类型上一篇:Go常量下一篇:运算符与表达式文章目录Go基本数据类型前言一Go数据类型二布尔类型`bool`三整数3.1有符号整数`int`3.2无符号整数`uint`3.3整数字面值四浮点类型4.1字面值4.1.1十进制浮点字面值4.1.2十六进制浮点字面值五复数......
  • DragonKnightCTF 2024
    misc签到用stegsolve发现二维码,扫码关注中学生CTF发送DragonKnight2024即可获得flag神秘文字一开始想着埃及文,找了半天发现很多都没有对应的字符,都开始摆了。但是,当我发给gpt后,发现是js混淆类的直接运行就拿到了压缩包密码了,然后解压就是flagcrypto签到分析之后......
  • 在Ubuntu中部署MongoDB数据库
    提示:为了方便,接下来的操作都在shell中进行(需提前建立ssh连接),当然也可以在虚拟机中进行。1.导入MongoDB的公钥首先导入MongoDB的公钥,以便后续下载和安装MongoDB输入如下代码wget-qO-https://www.mongodb.org/static/pgp/server-6.0.asc|sudoapt-keyadd-2.创建M......