更新记录
1.1.0(2025-07-06)
修复bug
1.0.0(2025-07-06)
1.0新发布
平台兼容性
uni-app x
Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
---|---|---|---|---|---|
- | - | 5.0 | × | × | × |
介绍
本插件仅适用于安卓,可以实现部署一个web页面,并可添加代理路径,或添加自定义处理路径,支持路径参数,如"/test/{page}/user/{id}/home"
。
下方示例均为uts语言调用,js调用时去除类型声明。
开始
通过调用createServer(port:number,host:string="0.0.0.0"):IWebFileServer
方法创建一个服务器实例
示例:
const server:IWebFileServer=createServer(5123,'0.0.0.0')
返回值:
IWebFileServer
接口定义了一组方法,用于操作和管理 Web 服务器,支持添加资源处理、设置跨域访问、修改静态文件路径等功能。该接口实现了通过 HTTP 服务器提供静态文件服务和处理动态请求的能力。
IWebFileServer接口方法及说明
1. start()
启动服务器。
返回值:
boolean
- 如果服务器启动成功,返回 true
,否则返回 false
。
示例:
let server:IWebFileServer=createServer(5123,'0.0.0.0')
if (server.start()) {
console.log('服务器启动成功');
} else {
console.log('服务器启动失败');
}
2. getHostname()
获取服务器监听的主机名。
返回值:
string
- 返回监听的主机名。
示例:
const hostname = server.getHostname();
console.log('服务器主机名:', hostname);
3. getListeningPort()
获取服务器监听的端口号。
返回值:
number
- 返回监听的端口号。
示例:
const port = server.getListeningPort();
console.log('服务器监听端口:', port);
4. isAlive()
检查服务器是否处于运行状态。
返回值:
boolean
- 如果服务器运行中,返回 true
,否则返回 false
。
示例:
if (server.isAlive()) {
console.log('服务器正在运行');
} else {
console.log('服务器未运行');
}
5. setCors(v: boolean)
设置服务器是否允许跨域请求。
参数:
v
(boolean):如果为true
,服务器将允许跨域访问。
示例:
server.setCors(true); // 允许跨域请求
6. stop()
关闭服务器。
示例:
server.stop(); // 停止服务器
7. addResourceHandler(method: string, uri: string, handler: IResourceHandler)
为指定的 URI 添加同步资源处理器,当定义的资源路径与代理路径重叠时将抛出异常。
参数:
method
(string):HTTP 请求方法,如"GET"
、"POST"
等。uri
(string):URI 路径,可以包含路径参数(如"/api/{test}/user/{id}"
)。handler
(session:ISession,response:IResponse)=>void:同步资源处理器回调函数,接收session
和response
两个参数。- ISession,IResponse定义查看下方
示例:
server.addResourceHandler(MethodType.GET,"/test/{page}/user/{id}/home",
function(session:ISession,response:IResponse):void{
//获取路径参数page
console.log(session.pathVariable["page"])
//获取路径参数id
console.log(session.pathVariable["id"])
//获取请求体
console.log(session.body)
//获取请求体解析后的json对象(若content-type为text/json)
console.log(session.json)
//获取查询参数
console.log(session.params)
//获取请求头
console.log(session.headers)
//获取请求cookie
console.log(session.cookie)
//设置返回文件
// response.body_file="/storage/emulated/0/Android/data/包名/apps/__UNI__xxxxx/www/static/assets/nanohttpd/default-mimetypes.properties"
//设置返回json对象,将自动解析为json字符串
// response.body_json={a:12,v:{b:"123"}}
//设置返回字符串
response.body_text="测试一下"
//设置返回头
response.header={"head1":"aaabbb","head2":3242,"head3":{name:"aadd"}}
})
8. addAsyncResourceHandler(method: string, uri: string, handler: IAsyncResourceHandler)
为指定的 URI 添加异步资源处理器,支持异步操作如网络请求、文件读取等,当定义的资源路径与代理路径重叠时将抛出异常。
参数:
method
(string):HTTP 请求方法,如"GET"
、"POST"
等,或使用MethodType.REQUEST
接收所有请求方法。uri
(string):URI 路径,可以包含路径参数(如"/api/{test}/user/{id}"
)。handler
(session:ISession,response:IResponse)=>Promise:异步资源处理器回调函数,返回 Promise。
特性说明:
- 异步支持:支持在处理器中执行异步操作,如网络请求、数据库查询等
- 错误处理:自动捕获异步操作中的错误并返回500状态码
- 性能优化:使用轮询机制等待异步操作完成,避免阻塞服务器
示例:
// 基本异步处理器
server.addAsyncResourceHandler(MethodType.GET, "/api/data/{id}",
async (session: ISession, response: IResponse): Promise<void> => {
try {
const id = session.pathVariable["id"]
console.log(`处理异步请求,ID: ${id}`)
// 模拟异步数据获取
const data = await fetchDataFromAPI(id)
response.body_json = {
success: true,
data: data,
timestamp: Date.now()
}
response.status = Status.OK
response.header = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
}
} catch (error) {
console.error(`异步处理失败: ${error}`)
response.body_json = {
success: false,
error: error.toString()
}
response.status = Status.INTERNAL_ERROR
}
})
// M3U8代理处理器示例
server.addAsyncResourceHandler(MethodType.REQUEST, "/m3u8-proxy",
async (session: ISession, response: IResponse): Promise<void> => {
const urlParamList = session.params["url"]
const urlParam = urlParamList != null && urlParamList.size > 0 ? urlParamList[0] : null
if (urlParam == null || urlParam == '') {
response.body_text = 'Missing url parameter'
response.status = Status.BAD_REQUEST
return
}
try {
const decodedUrl = decodeURIComponent(urlParam)
const result = await processM3U8Content(decodedUrl)
if (result.success) {
response.body_text = result.content ?? ''
response.header = {
'Content-Type': 'application/vnd.apple.mpegurl',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-cache'
}
response.status = Status.OK
} else {
response.body_text = result.error ?? 'M3U8处理失败'
response.status = Status.INTERNAL_ERROR
}
} catch (error) {
response.body_text = `M3U8处理异常: ${error}`
response.status = Status.INTERNAL_ERROR
}
})
// 模拟异步数据获取函数
async function fetchDataFromAPI(id: string): Promise<any> {
// 模拟网络延迟
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: id, name: "示例数据", value: Math.random() })
}, 100)
})
}
// 模拟M3U8内容处理函数
async function processM3U8Content(url: string): Promise<{success: boolean, content?: string, error?: string}> {
// 实际实现中这里会进行网络请求和内容处理
return { success: true, content: "#EXTM3U\n#EXT-X-VERSION:3\n..." }
}
ISession,IResponse,IResourceHandler,IAsyncResourceHandler定义
export type ISession={
/**请求的路径地址*/
uri:string
/**
* 获取请求查询参数,上传的表单参数也会存储于此,同名参数值存入同一列表
*/
params:MutableMap<string,MutableList<String>>
/**请求的查询参数原字符串*/
queryParameterString:string|null
/**请求头*/
headers:MutableMap<string,string>
/**请求的方法*/
method:string
/**远程主机名*/
remoteHostName:string
/**远程ip地址*/
remoteIpAddress:string
/**当为POST方法时,此处获取body*/
body:string|null
/**当为PUT方法时,此处为存储的文件的临时位置*/
tempFilePath:string|null
/**当为POST方法,且content-type为"application/json",且body不为null时,会自动解析body并存于body字段*/
json:UTSJSONObject|null
/** 获取cookie,Map类型,以cookie的name为键 */
cookie:MutableMap<string,string>
/**请求的路径参数,为Map类型,值始终为string类型,若需其它类型需自行转换*/
pathVariable:MutableMap<string,string>
}
export type IResponse={
/**状态码,默认为OK(200),可取值参照nanohttpd中Status枚举值*/
status:number
/** 当该值不为null时,将读取该路径下文件并返回,路径应为文件的绝对路径,此时body_text、body_json字段将被无视,注意,当文件位于公共存储空间时需申请文件读取权限 */
body_file:string|null
/** 当该值不为null时,且body_file为null时,将该值转为json字符串返回,此时body_text字段将被无视 */
body_json:any|null
/** 当该值不为null时,且body_file、body_json为null时,将该值作为响应体返回*/
body_text:string|null
/**响应的content-type值,为null时自动设置,默认值为null*/
mimetype:string|null
/** 响应头,默认值为null*/
header:UTSJSONObject|null
}
/**
* 同步资源处理器接口
*/
export type IResourceHandler = (session: ISession, response: IResponse) => void
/**
* 异步资源处理器接口
* 支持异步操作,如网络请求、文件读取等
*/
export type IAsyncResourceHandler = (session: ISession, response: IResponse) => Promise<void>
8. addProxyPath(uri: string, targetHost: string)
添加一个代理路径,将请求转发到目标服务器,当定义的代理路径与资源路径重叠时将覆盖资源路径。
参数:
uri
(string):本地服务器的 URI 路径。targetHost
(string):目标服务器的地址。
示例:
//对于该服务器的请求如"/proxy/api/user/get/info"将自动请求"www.proxy.example:12345/user/get/info"并返回响应
server.addProxyPath('/proxy/api', 'http://www.example.com');
9. addVideoStreamProxy(uri: string, targetHost: string, options?: IVideoStreamProxyOptions)
添加视频流代理转发路径,专门针对视频流进行优化,支持断点续传、流式传输等特性。
参数:
uri
(string):本地服务器的 URI 路径。targetHost
(string):目标视频流服务器的地址。options
(IVideoStreamProxyOptions, 可选):视频流代理配置选项。
IVideoStreamProxyOptions 配置选项:
enableRangeRequest
(boolean, 默认: true):是否启用Range请求支持(断点续传)bufferSize
(number, 默认: 8192):缓冲区大小(字节)connectionTimeout
(number, 默认: 30000):连接超时时间(毫秒)readTimeout
(number, 默认: 60000):读取超时时间(毫秒)enableStreaming
(boolean, 默认: true):是否启用流式传输supportedMimeTypes
(string[], 默认: ["video/mp4", "video/webm", "video/ogg", "application/vnd.apple.mpegurl", "application/dash+xml"]):支持的视频格式MIME类型列表
示例:
// 基本用法
server.addVideoStreamProxy('/video', 'http://video.example.com:8080');
// 高级配置
const videoOptions: IVideoStreamProxyOptions = {
enableRangeRequest: true,
bufferSize: 16384,
connectionTimeout: 30000,
readTimeout: 120000,
enableStreaming: true,
supportedMimeTypes: ['video/mp4', 'application/vnd.apple.mpegurl']
};
server.addVideoStreamProxy('/stream', 'http://streaming.server.com', videoOptions);
// 访问示例:
// http://localhost:8080/video/movie.mp4 -> http://video.example.com:8080/movie.mp4
// http://localhost:8080/stream/live.m3u8 -> http://streaming.server.com/live.m3u8
特性说明:
- 断点续传:支持HTTP Range请求,实现视频的断点续传功能
- 流式传输:支持大文件的流式传输,减少内存占用
- 多格式支持:自动识别MP4、WebM、OGG、HLS(m3u8)、DASH(mpd)等视频格式
- 智能缓存:保留重要的缓存相关头部信息
- 错误处理:完善的错误处理和日志记录
10. updateStaticFilePathType(staticFilePathType: number)
更新静态文件的路径类型,路径类型默认为Assets,可选值:Assets,Static,Public。 分别表示应用内部存储空间,应用外部存储空间,公共存储空间。
参数:
staticFilePathType
(number):路径类型,可以是StaticPathType.Assets
、StaticPathType.Static
或StaticPathType.Public
。
示例:
server.updateStaticFilePathType(StaticPathType.Static);
10. updateStaticFilePath(path: string)
更新静态资源文件的路径,当前路径类型将影响更新路径的过程。
参数:
path
(string):新的静态文件路径。
示例:
server.updateStaticFilePath('assets/static');
11. getStaticFilePath()
获取当前静态资源文件的路径。
返回值:
string
- 当前静态文件路径,需结合路径类型判断实际文件路径。
示例:
const staticFilePath = server.getStaticFilePath();
console.log('静态文件路径:', staticFilePath);
12. getStaticFilePathType()
获取当前静态文件路径的类型。
返回值:
number
- 静态文件路径类型,值可以是 0
(Assets)、1
(Static)、2
(Public)。
示例:
const staticFilePathType = server.getStaticFilePathType();
console.log('静态文件路径类型:', staticFilePathType);
13. updateStaticMimeFilePath(path: string)
更新 Mime 类型配置文件的位置。
参数:
path
(string):Mime 类型文件的路径,应该是父目录路径。
示例:
server.updateStaticMimeFilePath('/static/assets/nanohttpd');
导出方法
setLogLevel(level:number)
设置服务器自带日志的输出等级
参数:
level
(number):服务器内置日志的输出等级
导出类
可用其中的静态属性,方便赋值,插件更新时也可提供更好的兼容性 包含:StaticPathType,MethodType,LogLevel,Status
完整uts语言调用演示如下,js调用时需将类型声明去除:
<template>
<view style="height: 100%;">
<view style="height: 50%;">
<web-view ref="web" src="http://127.0.0.1:5123"></web-view>
</view>
<scroll-view>
<button @click="refresh_page" class="bt">刷新页面</button>
<button @click="open_server" class="bt">开启服务器</button>
<button @click="is_alive" class="bt">是否运行:{{alive}}</button>
<button @click="get_port" class="bt">获取监听端口:{{port}}</button>
<button @click="get_host" class="bt">获取监听地址:{{host}}</button>
<button @click="stop_server" class="bt">关闭服务器</button>
</scroll-view>
</view>
</template>
<script>
import { createServer,IWebFileServer,StaticPathType,MethodType,IResourceHandler,IAsyncResourceHandler,ISession,IResponse,setLogLevel,LogLevel,Status } from "@/uni_modules/xzhao-uts-LocalHttpServer"
let server:IWebFileServer=createServer(5123,'0.0.0.0')
export default {
data() {
return {
server:null,
alive:false,
port:-1,
host:''
}
},
onLoad() {
console.log(server)
//设置日志等级
setLogLevel(LogLevel.ALL)
//更新静态资源路径类型
server.updateStaticFilePathTypeWithPath(StaticPathType.Static,"assets/static")
// server.updateStaticFilePath("public")
console.log("静态资源路径为:",server.getStaticFilePath())
console.log("静态资源路径类型为:",server.getStaticFilePathType())
//更新mimetype文件路径
server.updateStaticMimeFilePath("/storage/emulated/0/Android/data/包名/__UNI__xxxxxx/www/static/assets/mime/")
//设置允许跨域访问本服务器
server.setCors(true)
//添加同步路径处理
server.addResourceHandler(MethodType.GET,"/test/{page}/user/{id}/home",
function(session:ISession,response:IResponse):void{
console.log(session.pathVariable["page"])
console.log(session.pathVariable["id"])
console.log(session.body)
console.log(session.json)
console.log(session.params)
console.log(session.headers)
console.log(session.cookie)
// response.body_file="/storage/emulated/0/Android/data/包名/apps/__UNI__xxxxx/www/static/assets/nanohttpd/default-mimetypes.properties"
// response.body_json={a:12,v:{b:"123"}}
response.body_text="测试一下"
response.header={"head1":"aaabbb","head2":3242,"head3":{name:"aadd"}}
response.status=Status.OK
})
//添加异步API处理器
server.addAsyncResourceHandler(MethodType.GET, "/api/user/{id}",
async (session: ISession, response: IResponse): Promise<void> => {
try {
const userId = session.pathVariable["id"]
console.log(`获取用户信息,ID: ${userId}`)
// 模拟异步数据库查询
const userData = await this.fetchUserData(userId)
response.body_json = {
success: true,
user: userData
}
response.status = Status.OK
response.header = {
"Content-Type": "application/json"
}
} catch (error) {
console.error(`获取用户数据失败: ${error}`)
response.body_json = {
success: false,
error: "用户数据获取失败"
}
response.status = Status.INTERNAL_ERROR
}
})
//添加M3U8代理处理器
server.addAsyncResourceHandler(MethodType.REQUEST, "/m3u8-proxy",
async (session: ISession, response: IResponse): Promise<void> => {
const urlParamList = session.params["url"]
const urlParam = urlParamList != null && urlParamList.size > 0 ? urlParamList[0] : null
if (urlParam == null || urlParam == '') {
response.body_text = 'Missing url parameter'
response.status = Status.BAD_REQUEST
return
}
try {
const decodedUrl = decodeURIComponent(urlParam)
const m3u8Content = await this.fetchM3U8Content(decodedUrl)
response.body_text = m3u8Content
response.header = {
'Content-Type': 'application/vnd.apple.mpegurl',
'Access-Control-Allow-Origin': '*'
}
response.status = Status.OK
} catch (error) {
response.body_text = `M3U8代理失败: ${error}`
response.status = Status.INTERNAL_ERROR
}
})
//添加代理路径
server.addProxyPath("/proxy/test/","http://127.0.0.1:12345")
//添加视频流代理
server.addVideoStreamProxy("/video", "http://video.example.com:8080", {
enableRangeRequest: true,
bufferSize: 16384,
enableStreaming: true,
supportedMimeTypes: ["video/mp4", "application/vnd.apple.mpegurl"]
})
//测试代理路径
let targetServer=createServer(12345)
targetServer.addResourceHandler(MethodType.REQUEST,"/be/proxy",
function(session:ISession,response:IResponse):void{
console.log("接收到请求")
response.body_json={"目标代理服务器":"返回结果","dwad":123123,"89879":["wadad"]}
})
targetServer.start()
},
methods: {
// 模拟异步数据获取函数
async fetchUserData(userId: string): Promise<any> {
// 模拟网络延迟
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: userId,
name: "用户" + userId,
email: `user${userId}@example.com`
})
}, 200)
})
},
async fetchM3U8Content(url: string): Promise<string> {
// 实际实现中这里会进行网络请求
return "#EXTM3U\n#EXT-X-VERSION:3\n#EXTINF:10.0,\nsegment1.ts\n#EXT-X-ENDLIST"
},
refresh_page(){
let w=this.$refs["web"] as UniWebViewElement
w.reload()
},
open_server(){
server.start()
},
get_host(){
this.host=server.getHostname()
},
get_port(){
this.port=server.getListeningPort()
},
is_alive(){
this.alive=server.isAlive()
},
stop_server(){
server.stop()
}
}
}
</script>
<style>
.logo {
height: 100px;
width: 100px;
margin: 100px auto 25px auto;
}
.bt {
font-size: 18px;
color: #8f8f94;
text-align: center;
}
</style>