使用OpenResty搭建DataEase视频服务器

编者注:本文为知乎博主万梓良的原创文章。
原文链接:
https://zhuanlan.zhihu.com/p/490108585。

DataEase是飞致云公司旗下的一款开源数据可视化分析工具,初代产品是在2021年6月份正式发布的。我是在从这款产品v1.4版本的时候入坑的,被它的功能所深深吸引,从此每月都紧跟产品的每月更新迭代。

之后DataEase v1.5版本更新了支持视频组件的功能,能够支持MP4格式和WebM格式的视频,刚好我公司想在制作的仪表板上播放公司的宣传视频,而DataEase的视频组件是通过直接嵌入视频链接的形式。具体如下图所示:

在这里插入图片描述

因此,公司内部想使用视频组件就需要搭建自己的视频文件服务器了。传统的文件服务器有很多,比如 Apache、Nginx等,不过这种方式搭建的文件服务器存在一个共性的问题,就是用户想要上传文件只能连接到服务器上,将文件上传到指定的目录。

这就需要将文件服务器的用户名/密码提供给所有需要使用到该文件服务器的人。这样操作一方面使用起来不是很便捷,特别是对业务人员来说,还需要去学习Linux的相关知识;另一方面,服务器用户名/密码的大量披露也会带来相应的安全隐患。

除此之外,也可以单独开发一套文件上传系统,但是带来的人力成本比较高,领导觉得性价比不高不予同意。

基于以上种种原因,公司希望能够搭建一个无需过多额外开发,且可以通过浏览器上传文件的文件服务器。带着这样的问题,我在网上一通搜索,发现了OpenResty。

OpenResty是一个基于Nginx与Lua的高性能Web平台,其内部集成了大量精良的Lua库、第三方模块以及大多数的依赖项。OpenResty可用于方便地搭建能够处理超高并发、扩展性极高的动态Web应用、Web服务和动态网关。

OpenResty通过汇聚各种设计精良的Nginx模块,从而将Nginx有效地变成一个强大的通用Web应用平台。这样,Web开发人员和系统工程师可以使用Lua脚本语言调动Nginx支持的各种C以及Lua模块,快速构造出足以胜任10K乃至1000K以上单机并发连接的高性能Web应用系统。

安装步骤

接下来我来演示一下使用OpenResty搭建DataEase视频服务器的具体步骤。

■ 安装OpenResty

官方提供了OpenResty的Docker镜像,执行以下命令获取最新的Docker镜像:

docker pull openresty/openresty

镜像拉取完成后,执行以下命令启动容器:

docker run -itd --name openresty -p 80:80   openresty/openresty:latest

容器启动完成后,访问IP,若出现如下页面,则说明启动成功:

在这里插入图片描述

■ 设置OpenResty

接着来调整OpenResty的基础配置,使其先成为一个普通的文件服务器。由于OpenResty镜像未内置vi/vim等编辑器,所以我们需要先在外部进行配置文件的编写,再将配置文件拷贝至容器内部。

配置文件如下所示:

autoindex on;# 显示目录
autoindex_exact_size on;# 显示文件大小
autoindex_localtime on;# 显示文件时间

server {
    listen  80 ;
    charset utf-8;
    root         /data/;

    location / {
    }
}

将配置文件保存为default.conf ,执行以下命令拷贝到容器内部:

docker cp default.conf openresty:/etc/nginx/conf.d

接着重启容器:

docker restart openresty

重启完成后进入容器,创建我们配置文件中写的 /data 目录:

docker exec -it openresty bashmkdir /data

接着打开浏览器,访问 http://IP ,看下效果:

在这里插入图片描述

接着我们拷贝任意文件至容器内的 /data 目录,看下效果:

在这里插入图片描述

docker cp default.conf openresty:/data

可以看到,文件名称、上传时间、大小等已经在我们的浏览器中展示出来了。至此,基础的文件服务器就搭建完成了。

■ 设置页面文件上传

接下来我们来编写lua脚本,实现页面文件上传,并且展示上传进度的功能。

首先是页面嵌入脚本:

vi inject.lua
-- ignore *.html files
if ngx.var.uri:match(".html$") then
    return "</body>"
end

local inject_div = [[
<div id="upload-inject">
<link rel="stylesheet" href="/code/iview.css">
<script src="/code/vue.min.js"></script>
<script src="/code/iview.min.js"></script>
<style>a {color: -webkit-link}</style>
<div id="app-upload">
<upload
    multiple
    type="drag"
    :action="uploadUrl"
    :on-success="success"
    :before-upload="beforeUpload">
    <div style="padding: 20px 0">
        <icon type="ios-cloud-upload" size="52" style="color: #3399ff"></icon>
        <p>点击或拖拽文件上传</p>
    </div>
</upload>
</div>
<script>
new Vue({
    el: '#app-upload',
    data: {
        uploadUrl: ''
    },
    mounted() {
        this.uploadApi = '/_upload'
    },
    methods: {
        beforeUpload: function() {
            this.uploadUrl = this.uploadApi + window.location.pathname
            let promise = new Promise((resolve) => {
                this.$nextTick(function () {
                    resolve(true);
                })
            })
            return promise
        },
       success: function(){
         console.log("success");
         location.reload();

       }
    }
})
</script>
<div>
</body>
]]

return inject_div

然后是上传文件脚本:

vi upload.lua
local upload = require "resty.upload"
local cjson = require "cjson"


local chunk_size = 4096
local home = "/data"


local form, err = upload:new(chunk_size)
if not form then
    ngx.log(ngx.ERR, "failed to new upload: ", err)
    ngx.exit(500)
end

form:set_timeout(1000) -- 1 sec

local function getsubdir(uri)
    return uri:gsub("^/_upload", "")
end

local function split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match);
    end
    return result;
end

local function strip(s)
        char = "%s"
        return string.match(s, "^" .. char .. "*(.-)" .. char .. "*$") or s
end

local function startswith(line, s)
    return line:find("^" .. s) ~= nil
end

local function getfilename(line)
    items = split(line, ";")
    for i, item in ipairs(items) do
        item = strip(item)
        if startswith(item, "filename") then
            name = split(item, "=")
            return name[2]:sub(2, -2)
        end
    end
    return ""
end

local filename = ""
local subdir = getsubdir(ngx.var.uri)
local file

while true do
    local typ, res, err = form:read()
    if not typ then
        ngx.say("failed to read: ", err)
        ngx.exit(500)
    end        
    if typ == "header" then
        if res[1] == "Content-Disposition" then
            filename = getfilename(res[2])
            if filename == "" then
                ngx.say("filename not found")
                ngx.exit(400)
            end
            
            local path = home .. subdir .. "/" .. filename
            file = assert(io.open(path, "w+"))
            if not file then
                ngx.say("open " .. path .. " failed")
                ngx.exit(500)
            end
        end
    elseif typ == "body" then
        if file then
            file:write(res)
        end
    elseif typ == "part_end" then
        if file then
            file:close()
            file = nil
        end
    elseif typ == "eof" then
        break
    end
end

ngx.header.content_type = "text/html"
ngx.say("<p>upload success</p>")
ngx.flush(true)

除此之外,我还引用了一些外部的CSS、JavaScript、Fonts文件,文件的分享地址如下。有需要可以自行下载:

链接:

https://pan.baidu.com/share/init?surl=FWt3HeY8Xy0OuZ6VJbeGiw

密码:up1j

我将这些文件拷贝至OpenResty容器内部:

docker cp code/ openresty:/

接着调整我们的default.conf文件,同样拷贝至容器内部:

vi default.conf

autoindex on;# 显示目录
autoindex_exact_size on;# 显示文件大小
autoindex_localtime on;# 显示文件时间

server {
    listen  80 ;
    charset utf-8;
    root         /data/;


    location /code/ {
        alias /code/;
    }

    location / {
        sub_filter_once on;
        set_by_lua_file $inject_div_before_body /code/inject.lua;
        sub_filter '</body>' $inject_div_before_body;
    }


    location ~ ^/_upload {
        client_max_body_size 4000m;
        content_by_lua_file /code/upload.lua;
    }

}


docker cp default.conf openresty:/etc/nginx/conf.d

再次访问 http://IP :

在这里插入图片描述

页面拖拽文件上传:

在这里插入图片描述
在这里插入图片描述

至此,可以通过浏览器上传文件的文件服务器便制作完成了。接下来,我们就可以复制视频链接,并且在仪表板上播放视频了。

最终效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

现在,一个简单、页面拖拽即可完成视频上传的文件服务器就此完成了。搭配上人人可用的开源数据可视化分析平台DataEase,完美解决了公司的需求!

注:文中的lua脚本参考了GitHub上大佬的代码,仓库地址如下:/http://github.com/yangbinnnn/ngx-upload-web。我在其基础上做了文件上传目录优化、上传完成自动刷新、本地化第三方组件等功能。

相关推荐

防火墙开着,Docker端口却没拦住?1Panel几步搞定!

1Panel开源面板支持“容器端口防护”功能。

FIT2CLOUD飞致云的博客 63

大幅提升本地模型部署体验,1Panel开源面板v2.5.0版本发布

1Panel AI一体机(方案六)服务器版实测数据汇总。

FIT2CLOUD飞致云的博客 78

测评丨DeepSeek-V4.1-Flash-1017企业私有化部署实测报告

1Panel AI一体机(方案六)服务器版实测数据汇总。

FIT2CLOUD飞致云的博客 242

支持千问安全围栏模型,1Panel AI网关v1.6.0版本发布

新增上游账号多API Key、同一请求模型多映射、OpenAI兼容图像编辑接口、实时用户活动卡片与版本更新通知。

FIT2CLOUD飞致云的博客 288

飞致云开源社区月度动态报告(2026年10月)

为数智经济时代创造好软件。

FIT2CLOUD飞致云的博客 113

新增语音API与模型管理功能,1Panel AI网关v1.5.0版本发布

上游账号管理支持多种协议类型与错误历史查看。

FIT2CLOUD飞致云的博客 450

持续提升系统安全性与用户体验,JumpServer堡垒机v4.10.20 LTS版本发布

欢迎广大用户升级至新版本。

FIT2CLOUD飞致云的博客 255

安全漏洞和已知问题修复,MaxKB开源企业级智能体平台v2.10.8 LTS版本发布

欢迎升级至新版本MaxKB。

FIT2CLOUD飞致云的博客 191

支持SMB远程文件数据源,查询组件支持多条件搜索,DataEase开源BI工具v3.2.0版本发布

透视表条件样式支持指定范围,汇总表支持图片链接渲染与放大预览。

FIT2CLOUD飞致云的博客 81

离线安装方式上线,支持Token Plan,1Panel AI网关v1.4.0版本发布

进一步提升AI网关部署的灵活性与日常运维体验。

FIT2CLOUD飞致云的博客 186

KoKo、Tinker组件重构,会话录像支持批量下载,JumpServer开源堡垒机v5.1.0版本发布

命令过滤规则支持AI辅助起草

FIT2CLOUD飞致云的博客 182

修复多个安全漏洞,MaxKB开源企业级智能体平台v2.10.7 LTS版本发布

欢迎升级至新版本MaxKB。

FIT2CLOUD飞致云的博客 174

学习笔记丨MaxKB PPT/PDF文档整页转图工作流知识库方案

解决复杂图文文档知识入库难题。

FIT2CLOUD飞致云的博客 296

飞致云开源社区月度动态报告(2026年9月)

为数智经济时代创造好软件。

FIT2CLOUD飞致云的博客 248

1Panel AI网关智能路由新增支持Jev模式

面向多个自定义业务目标进行语义决策。

FIT2CLOUD飞致云的博客 297

电子表格支持嵌入集成,支持GaussDB数据源与StarRocks计算引擎,DataEase开源BI工具v3.1.0版本发布

新增箱线图图表类型,修复7项安全漏洞。

FIT2CLOUD飞致云的博客 450

操作教程|通过1Panel应用商店快速安装Laya

Laye开源模型已上架至1Panel应用商店。

FIT2CLOUD飞致云的博客 210

修复安全漏洞,支持箱线图与汇总表合并单元格,DataEase开源BI工具v2.10.27 LTS版本发布

公共链接支持独立访客权限控制,数据填报与定时报告功能增强。

FIT2CLOUD飞致云的博客 383

强化安全性,Text-to-SQL智能选表升级,SQLBot开源智能问数系统v1.10.2版本发布

强化账号安全初始化机制,新增泰语支持。

FIT2CLOUD飞致云的博客 438

实战丨飞致云面向自身销售场景打造专属CRM Skills

自然语言驱动CRM智能查询、创建、更新、转化、链路追踪与漏斗分析。

FIT2CLOUD飞致云的博客 239

跨越新高度:JumpServer V5 来了!

统一工作台,拥抱AI新时代!

FIT2CLOUD飞致云的博客 114

测评|双卡RTX Pro 5000 Blackwell+Qwen3.8-Flash-Next:WorkBuddy办公场景实测

业务场景一手测试数据解读。

FIT2CLOUD飞致云的博客 285

支持多种登录认证方式、支持MFA与重排模型,1Panel AI网关功能上新

新增支持为上游账号配置代理和更多实用功能。

FIT2CLOUD飞致云的博客 124

案例研究|中国土木构建“财小问”智能体,提升财务管理效率

MaxKB助力财务部门从重复劳动迈向智能决策。

FIT2CLOUD飞致云的博客 97

联合方案丨MaxKB+DataEase实现智能跨库问数

通过自然语言提问,在权限范围内获取一站式的可视化数据以及数据洞察结论。

FIT2CLOUD飞致云的博客 220

GPU监控支持华为昇腾设备,防火墙管理焕新,虚拟机管理下放至专业版,1Panel v2.3.0版本发布

持续完善1Panel在AI模型部署与硬件适配方面的能力。

FIT2CLOUD飞致云的博客 256

免费AI网关测评:LiteLLM、New API和1Panel AI网关

为企业运维团队、AI业务团队提供选型参考。

FIT2CLOUD飞致云的博客 291

告别积分焦虑!WorkBuddy+Qwen3.8-Flash-Next本地部署,实现AI办公自由

把算力自由握在自己手里。

FIT2CLOUD飞致云的博客 420

工作流支持默认模型设置,MaxKB开源企业级智能体平台v2.10.6 LTS版本发布

腾讯混元、腾讯云模型供应商合并为新“腾讯云”供应商。

FIT2CLOUD飞致云的博客 347

1Panel AI网关现已开放,10人及以下团队免费使用

助力小团队低成本落地AI规模化应用。

FIT2CLOUD飞致云的博客 385
上一篇: 案例分享|中国移动上研院基于MeterSphere开源持续测试平台构建规范化测试体系
下一篇: 新增Magnus组件,支持数据库代理直连方式连接数据库,支持分布式策略访问资产,JumpServer堡垒机v2.21.0发布
FIT2CLOUD飞致云
FIT2CLOUD飞致云 企业官方账号 企业官方账号
博客等级 码龄7年 3193粉丝 741原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值