import 'dart:convert';
import 'dart:io';
ContentType jsType = ContentType('application','javascript',charset:'UTF-8');
ContentType textType = ContentType('text','plain',charset:'UTF-8');
ContentType htmlType = ContentType('text','html',charset: 'UTF-8');
List<int> disposition = 'Content-Disposition:'.codeUnits;
List<int> multipart = 'Content-Type:'.codeUnits;
List<int> u8x2crlf = '\r\n\r\n'.codeUnits;
List<int> u8crlf__ = '\r\n--'.codeUnits;
const styleTag = '''<style>
body {background:black; color:green; font-size:16px}
a {margin-left:32px}
.buttonLike {background:black; color:dodgerblue; cursor:pointer}
.buttonLike:hover {background:gray; color:white}
</style>''';// css
const buttonJS = '''<input hidden id="pickOne" type="file">
<label for="pickOne" class="buttonLike" id="choose">上傳檔案 ?</label>
<button id="uploadButton" onclick="uploadFile()" disabled="true"></button><br>
<script type="application/javascript">
pickOne.addEventListener("change", (e)=> {
choose.innerHTML = e.target.files[0].name;
uploadButton.disabled = false;
uploadButton.innerHTML= "開始上傳";
});
const uploadFile = () => {
if (pickOne.files.length > 0) {
pickOne.disabled = true;
uploadButton.disabled = true;
uploadButton.textContent = "上傳中, 請稍候...";
const formData = new FormData();
formData.append("appAPK", pickOne.files[0]);
fetch("/postTMP", {method: "POST", body: formData})
.then((blob) => console.log("上傳成功."))
.catch((error) => console.log("上傳失敗!"))
.finally(() => location.reload());
}
}
</script>''';
const headTag = '<head><meta charset="UTF-8">$styleTag</head>';
const endbodyhtml = '</body></html>';
const htmlHere='<!DOCTYPE html><html>$headTag<body>It works.</body></html>';
int indexof(List<int> data, List<int> subList) {
int matchlength = subList.length;
int size = data.length;
int slider = 0;
while (slider < size) {
int endPos = slider + matchlength;
if (endPos > size) return -1;
int position = slider;
for (int i = 0; i < matchlength; i++) {
if (data[position] != subList[i]) break;
position ++;
}
if (endPos == position) return slider;
slider ++;
}
return -1;
}
String formParse (List<int> data, String key) {
List<String> args = utf8.decode(data).trim().split(';');
for (final pair in args) {
if (! pair.contains(key) || ! pair.contains('=')) continue;
int start = pair.indexOf('"');
int last = pair.lastIndexOf('"');
if (start >= 0 && last > 1) return pair.substring(start + 1, last);
start = pair.indexOf("'");
last = pair.lastIndexOf("'");
if (start >= 0 && last > 1) return pair.substring(start + 1, last);
return pair.split('=').last;
}
return '';
}
void postListen (HttpRequest req, String path) {
Directory uploadDir = Directory(path);
String? tag = req.headers.contentType?.parameters['boundary'];
if (tag == null) req.response.close();
else {
List<int> boundary = tag.codeUnits;
IOSink? sink = null;
req.listen (
(List<int> data) {
int start = indexof(data, boundary);
if (start < 0) sink ?. add(data);
else {
start += 2 + boundary.length;
List<int> form = data.sublist(start);
int offset = indexof(form, disposition);
if (offset >= 0) {
offset = indexof(form, u8x2crlf);
if (offset >= 0) {
start += offset + u8x2crlf.length;
int eos = indexof(form, multipart);
if (eos > 0) {
if (! uploadDir.existsSync()) {
uploadDir.createSync(recursive: true);
}
String name = formParse(form.sublist(0, eos), 'filename');
if (name.length > 0) {
sink = File('${uploadDir.path}/$name').openWrite();
}
}
}
}
final startData = data.sublist(start);
int size = indexof(startData, boundary);
if (size >= 0) {
if (indexof(startData, u8crlf__) > 0) size -= 4;
sink ?. add(data.sublist(start, start + size));
} else {
if (offset > 0) {
sink ?. add(startData);
} else {
size = indexof(data, boundary);
if (indexof(data, u8crlf__) > 0) size -= 4;
sink ?. add(data.sublist(0, size));
}
}
}
},
onError: (_) => req.response.close(),
onDone : ( ) {
req.response .. statusCode = 200 .. write('OK') .. close();
sink ?. close();
}
);
}
}
void getPipe(String pathname, HttpResponse reply) {
String fname = (pathname == '/index') ? './index.html' : '.$pathname';
final name = Uri.decodeFull(fname);
File file2Reply = File(name);
reply .. statusCode = 200 .. headers.contentType = htmlType;
if (! file2Reply.existsSync() && ! name.endsWith('/')) {
if (name == './hello' || name == './index.html') reply.write(htmlHere);
else reply .. statusCode = 404 ..
write('<!DOCTYPE html><html><body>404 Not found!</body></html>');
reply.close();
return;
}
if (name.endsWith('/')) {
final dir = Directory(name);
reply.write('<!DOCTYPE html><html>$headTag<body>$buttonJS 檔案清單:<br>');
if (dir.existsSync()) dir.listSync() ..
sort((a, b) {
if (a is Directory && b is File) return 1;
if (a is File && b is Directory) return -1;
return a.path.toLowerCase().compareTo(b.path.toLowerCase());
}) ..
forEach((FileSystemEntity e) {
final url = Uri.encodeFull(e.path.replaceFirst('.', ''));
final show = e.path.replaceFirst(name , '');
if (e is File) reply.write('<a href="$url"> 檔案 $show</a><br>');
if (e is Directory) reply.write('<a href="$url/"> 目錄 $show</a><br>');
});
reply .. write(endbodyhtml) .. close();
return;
}
reply ..
headers.contentLength = file2Reply.lengthSync() ..
headers.contentType = name.endsWith('.js') ? jsType :
RegExp(r'(\.htm|\.html)$').hasMatch(name) ? htmlType :
RegExp(r'(\.txt|\.dart|\.pem|\.c|\.h)$').hasMatch(name) ? textType :
name.endsWith('.css') ? ContentType('text', 'css') :
name.endsWith('.jpg') ? ContentType('image', 'jpg') :
name.endsWith('.png') ? ContentType('image', 'png') :
ContentType('application', 'octet-stream');
file2Reply.openRead().pipe(reply);
}
void main(List<String> args) {
final ip = InternetAddress.anyIPv4;
int port = args.length > 0 ? int.parse(args[0]) : 8080;
print('http://${ip.host}:$port');
HttpServer.bind(ip, port).then((HttpServer server){
server.listen((HttpRequest req) {
String pathname = req.uri.path;
if (req.method == 'GET') getPipe(pathname, req.response);
else if(pathname =='/postTMP') postListen(req, './上傳暫存區');
else req.response.close();
});
ProcessSignal.sigint.watch().listen((ProcessSignal signal) {// Ctrl + C
server.close();
exit(1);
});
});
}
2022年12月16日 星期五
用 dart 語言寫簡易帶上傳功能的 http 檔案伺服器
訂閱:
張貼留言 (Atom)
Linux mint 玩 waydroid 一些心得
1. 目前使用 linux mint 22.1 作業系統可以順利跑起來, 可上官網去下載, 並安裝到硬碟. 2. 安裝 waydroid 可上網站 https://docs.waydro.id 參考看看: https://docs.waydro.id/usage/inst...
-
1. 上 Firebase 網站 註冊啟用 Firebase : https://firebase.google.com/ 2. 進入 Firebase Console 註冊開啟新專案 : https://console.firebase.google.com/...
-
Flutter 讓人很容易短時間就能開發出 app, 關鍵在於他開發了很多種小部件稱為 Widget, 將Widget 組合起來就是一個 app. 所謂 部 件(Widget)就是一個可以呈現在螢幕上的視覺系 物 件,幾乎可以說 Flutter 每個物件都是 部 件. 開發者透...
沒有留言:
張貼留言