如何在 Flutter 里建立一个内部的 HTTP 服务器
1. 介绍
如果你想要让你的用户通过浏览器将文件上传到你的应用中,那就可以使用 Flutter
创建一个内部的 HTTP
服务器。这个功能非常有用,例如,你的应用要提供离线的视频或者音频播放,可以让用户上传自己的视频或音频文件。
2. 让我们开始吧
其实要有很多问题需要解决的,不过由于篇幅所限,因此我在本文中只会介绍几个重要的问题,但不用担心,我最终会提供一套完整的代码 🙂
好的,现在就让我们来解决以下的问题吧:
1) 创建 HTTP 服务器
我们可以使用 HttpServer
创建 HTTP
服务器,然后使用 bind
方法去设置其 IP 和端口:
final s = await HttpServer.bind(address, port);
然后监听所有的访问
s.listen(_handleReq);
_handleReq(HttpRequest request) async {
...
}
但下一个问题就是,我们要如何获取 IP 地址然后传递给 HttpServer
呢?
我们可以使用 NetworkInterface
列出当前所有网络,然后通过 IP 地址类型将 IPV4
的地址找出来
for (var interface in await NetworkInterface.list()) {
for (var addr in interface.addresses) {
if (addr.type == InternetAddressType.IPv4) {
currentIP = addr.address;
}
}
}
但你很可能会找到多个 IP 地址,而其中只有一个是正确的,所以我们还需要对 IP 地址进行过滤,只要以 192
开头的内网地址就好了。这个方法在 Android 里完全没问题,但在 iOS 中就会发现原来并不只有一个 IP 是以 192
开头的,这时我们就要再检测其网络接口名称是否以 en
开头以确定是我们真正需要的 IP,以下就是相关的代码:
for (var interface in await NetworkInterface.list()) {
for (var addr in interface.addresses) {
if (addr.type == InternetAddressType.IPv4 &&
(Platform.isIOS ? interface.name.startsWith('en') : 1 == 1) &&
addr.address.startsWith('192')) {
currentIP = addr.address;
}
}
}
2) 在 HTTP 服务器里运行 HTML 文件
我们要在此 HTTP
服务器里运行由 HTML
文件编写的前台网站,所以要解决如何处理 HTML
文件的问题。你可以将这个 HTML
网站放到 asset
目录里,然后更新 pubspec.yaml
文件
flutter:
assets:
- assets/
- assets/webserver/
- assets/webserver/css/
- assets/webserver/fonts/
- assets/webserver/js/
HTML 的网站目录结构如下:
assets
\webserver
\css
\fonts
\js
index.html
然后创建一个 Map
对象以存放请求的 HTML
文件
import 'dart:typed_data';
class AssetsCache {
/// Assets cache
static Map<String, ByteData> assets = {};
/// Clears assets cache
static void clear() {
assets = {};
}
}
基于上面的监听访问代码,我们要修改 _handleReq
方法:
首先,从请求的 URL 中获取当前要访问的文件
_handleReq(HttpRequest request) async {
String path = request.requestedUri.path.replaceFirst('/', '');
...
}
上面的代码将会从当前 URL 中取得访问文件的连接,如以下例子:
first request url: http://192.168.1.215:8080/index.html
second request url: http://192.168.1.215:8080/js/index.js
...
第一个请求的 path
的值就是 index.html
,第二个请求的值是 js/index.js
,所以我们可以创建一个 _loadAsset
方法来处理,这个方法会返回请求文件所在的 webserver
文件夹里的实际文件路径
Future<ByteData> _loadAsset(String path) async {
if (AssetsCache.assets.containsKey(path)) {
return AssetsCache.assets[path]!;
}
if (_rootDir == null) {
// print('path=============');
// print(join(assetsBasePath, path));
ByteData data = await rootBundle.load(join(assetsBasePath, path));
return data;
}
if (await Directory(_rootDir.path).exists()) {}
debugPrint(join(_rootDir.path, path));
final f = File(join(_rootDir.path, path));
return (await f.readAsBytes()).buffer.asByteData();
}
然后将文件呈现在客户端
final data = await _loadAsset(path);
request.response.headers.add('Content-Type', '$mime; charset=utf-8');
request.response.add(data.buffer.asUint8List());
request.response.close();
最后,你就可以通过这个 HTTP 服务器访问你的 HTML 网站了
3) 从 HTML 上传文件到 Flutter
你可以使用任何的 ajax
方法以从客户端上传文件,但在这个例子中,我使用了 jquery file upload 插件,我就不介绍在客户端的详细做法了,大家可自行查看相关文档,在这里只介绍如何处理服务器端,即 Flutter 如何获取客户端的文件
当使用了 ajax
从客户端发送文件到服务器端后,我们可以在 Flutter 里使用 MimeMultipartTransformer
来获取文件
var transformer = MimeMultipartTransformer(
request.headers.contentType!.parameters['boundary']!);
var bodyStream = transformer.bind(request);
//获取文件对象
await for (var part in bodyStream) {
if (isFirstPart) {
isFirstPart = false;
} else {
//这里要特别说明一下,因为我们要支持上传大文件(超过500MB),所以要将文件分割成块进行上传,
//这时每一个块都会被添加上独立的 content-disposition 的文件头,因此在这里要将其他块里的
// content-disposition 移除,否则最终拼接出来的文件将不能读取
part.headers.remove('content-disposition');
}
var fileByte =
await part.fold<List<int>>(<int>[], (b, d) => b..addAll(d));
if (fileByte.length > 1) {
//我们只需要处理大于 0 字节的文件块
uploadFile.add(fileByte);
await Future.delayed(Duration.zero);
}
}
然后返回上传的状态
final response = {
'message': 'File uploadeding',
};
request.response
..statusCode = HttpStatus.ok
..headers.contentType = ContentType.json
..write(json.encode(response))
..close();
你可在这里查看完整的代码。
3. 使用 internal_http_server 包
好吧,如果你不想自己写代码实现的话,我已为你准备好了,我创建了 internal_http_server
这个包并且也发布到 pub.dev
上了,所以如果你想直接使用,可到这里查看详情 🙂
3.1 安装包
你可以直接在 pubspec.yaml
文件里添加以下代码:
internal_http_server: ^0.0.4
3.2 创建 HTTP 服务器
你可以用以下方法创建一个服务器
InternalHttpServer server = InternalHttpServer(
title: 'Testing Web Server',
address: InternetAddress.anyIPv4,
port: 8080,
logger: const DebugLogger(),
);
3.3 设置服务器的使用说明
你可以方便地设置你的 WEB 服务器的一些使用说明,好让用户知道要如何使用它。例如,你想让用户上传自己的文件,就可以做一些说明,这些说明使用的是 HTML 代码:
(由于我发布的包是英文版的,所以以下说明文本我就不另作改动和翻译了,大家根据自己情况修改即可)
final String server_description = ''' Description:
<ul>
<li>
You can put your <strong>webserver</strong> description here
</li>
<li>
It's support any <strong style='color:red'>HTML</strong>, you can describe what you want to say
</li>
</ul>
How to use:
<ul>
<li>1. You can drag and drop the file here or click the 'Upload File' button to upload</li>
<li>2. It's support larger file</li>
<li>3. You can upload multiple files once time</li>
</ul>
''';
server.setDescription(server_description);
3.4 创建启动与停止服务器的方法
你还需要为你的服务器创建一个启动和停止的方法:
startServer() async {
server.serve().then((value) {
setState(() {
isListening = true;
buttonLabel = 'Stop';
});
});
}
stopServer() async {
server.stop().then((value) {
setState(() {
isListening = false;
buttonLabel = 'Start';
});
});
}
然后再创建一个按钮进行启动和停止之用
ElevatedButton(
child: Text(buttonLabel),
onPressed: () {
if (isListening) {
stopServer();
} else {
startServer();
}
},
),
同时,你也需要让用户知道服务器的 IP,因此你还要需要获取当前设备 IP 然后告知用户。我们可以使用 NetworkInterface
来获取当前设备的网络信息:
Future<String> getCurrentIP() async {
// Getting WIFI IP Details
String currentIP = '';
try {
for (var interface in await NetworkInterface.list()) {
for (var addr in interface.addresses) {
print(
'Name: {interface.name} IP Address:{addr.address} IPV4: {InternetAddress.anyIPv4}');
if (addr.type == InternetAddressType.IPv4 &&
addr.address.startsWith('192')) {
currentIP = addr.address;
}
}
}
} catch (e) {
debugPrint(e.toString());
}
// print('currentIP========:currentIP');
return currentIP;
}
然后,你就可以将 IP 呈现给用户,用户将可看到类似下面的效果
然后当用户使用浏览器访问 IP 时,就会看到一个漂亮的 web 服务器页面了,此时就可以直接上传文件到手机
最后,你可在这里找到项目的完整代码。
4. 总结
实际上如果你想用 Flutter 自己建立一个 HTTP 内部服务器,还是有很多问题要解决的,因此我创建了这个独立的包可以让大家免费使用,但这里面也有很多功能需要完善,如现在还不支持无限子文件夹读取,但我的时间也有限,因此如果大家有好的想法也欢迎交流和一起改善 🙂
版权声明:
作者:winson
链接:https://www.coderblog.cc/2024/06/how-to-create-http-server-in-flutter/
来源:代码部落中文站
文章版权归作者所有,未经允许请勿转载。