QinGang_interested/lib/tools/asset_server.dart

99 lines
3.6 KiB
Dart

// lib/utils/asset_server.dart
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class AssetServer {
AssetServer._internal();
static final AssetServer _instance = AssetServer._internal();
factory AssetServer() => _instance;
HttpServer? _server;
int? _port;
bool get running => _server != null;
Future<Uri> start() async {
if (_server != null && _port != null) {
return Uri.parse('http://127.0.0.1:$_port');
}
// Bind to ephemeral port
_server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
_port = _server!.port;
_server!.listen((HttpRequest request) async {
try {
final path = request.uri.path.isEmpty ? '/map.html' : request.uri.path;
debugPrint('[AssetServer] -> Request: $path');
// map to assets/map/<path>
final assetPath = 'assets/map$path';
// simple content-type detection
final contentType = _contentTypeFromPath(path);
if (assetPath.endsWith('.html') || assetPath.endsWith('.htm')) {
// For HTML, we prefer to read as text
final html = await rootBundle.loadString('assets/map/map.html');
request.response.headers.contentType = ContentType('text', 'html', charset: 'utf-8');
request.response.add(utf8.encode(html));
await request.response.close();
return;
}
// Other assets as bytes
try {
final bd = await rootBundle.load(assetPath);
final bytes = bd.buffer.asUint8List();
if (contentType != null) request.response.headers.contentType = contentType;
request.response.add(bytes);
await request.response.close();
} catch (e) {
debugPrint('[AssetServer] asset not found: $assetPath -> $e');
request.response.statusCode = HttpStatus.notFound;
request.response.write('Not Found');
await request.response.close();
}
} catch (e) {
debugPrint('[AssetServer] internal error: $e');
try {
request.response.statusCode = HttpStatus.internalServerError;
request.response.write('Internal Server Error');
await request.response.close();
} catch (_) {}
}
});
debugPrint('[AssetServer] started at http://127.0.0.1:$_port');
return Uri.parse('http://127.0.0.1:$_port');
}
Future<void> stop() async {
try {
await _server?.close(force: true);
_server = null;
_port = null;
debugPrint('[AssetServer] stopped');
} catch (e) {
debugPrint('[AssetServer] stop error: $e');
}
}
ContentType? _contentTypeFromPath(String path) {
final lower = path.toLowerCase();
if (lower.endsWith('.js')) return ContentType('application', 'javascript', charset: 'utf-8');
if (lower.endsWith('.css')) return ContentType('text', 'css', charset: 'utf-8');
if (lower.endsWith('.html') || lower.endsWith('.htm')) return ContentType('text', 'html', charset: 'utf-8');
if (lower.endsWith('.json')) return ContentType('application', 'json', charset: 'utf-8');
if (lower.endsWith('.png')) return ContentType('image', 'png');
if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return ContentType('image', 'jpeg');
if (lower.endsWith('.svg')) return ContentType('image', 'svg+xml');
if (lower.endsWith('.gif')) return ContentType('image', 'gif');
if (lower.endsWith('.webp')) return ContentType('image', 'webp');
return ContentType('application', 'octet-stream');
}
}