Skip to content

Instantly share code, notes, and snippets.

@mitsuoka
Last active November 28, 2022 15:03
Show Gist options
  • Save mitsuoka/2659496 to your computer and use it in GitHub Desktop.
Save mitsuoka/2659496 to your computer and use it in GitHub Desktop.
Dart code sample: Simple file server and MIME type library
Dart code sample: Simple file server and MIME types library
*** Caution ***
Do not use this code for actual applications. You should use secure connection
and limit access to restricted areas of your file system.
Usage:
1. Download, uncompress and rename the folder to "FileDownloadServer".
2. Put files you want to send into the folder.
3. From Dart Editor, File > Open Folder and select this FileDownloadServer folder.
4. Run the FileServer.dart as server.
5. Access the server from your browser:
http://localhost:8080/fserver (Downloading though the download page. Enter the
file_name and click the submit button)
or, http://localhost:8080/fserver/file_name (Direct downloading)
How to specify the file_name:
You can specify the file_name using relative path or absolute path. For example,
let “C:/dart_apps_server/FileDownloadServer” is the folder path of the file
server and the “readme.text” file is placed in the same folder. Then “readme.text”
is equivalent to “C:/dart_apps_server/FileDownloadServer/readme.text”.
Likewise, “test/jpegImage.jpg” is equivalent to
“C:/dart_apps_server/FileDownloadServer/test/jpegImage.jpg”.
Description like “../HelloWorld/HelloWorld.dart” is limited to the download page
and that is equivalent to “C:/dart_apps_server/HelloWorld/HelloWorld.dart”.
In case of downloading HTML files through the download page, browsers will not execute
scripts in the downloaded HTML file.
<!DOCTYPE html>
<html>
<head>
<title>Request a File</title>
</head>
<body>
<h1>Request a file</h1>
<h2>Enter the file name to download</h2>
<!-- Modify following lines for your application -->
<form method="get" action="http://localhost:8080/fserver">
<input type="text" name="fileName" value="readme.txt" size="50">
<br>
<input type="submit" value=" Submit ">
</form>
</body>
</html>
/*
Dart code sample: Simple file server
1. Download, uncompress and rename the folder to 'FileDownloadServer'.
2. Put files you want to send into the folder.
3. From Dart Editor, File > Open Folder and select this FileDownloadServer folder.
4. Run the FileServer.dart as server.
5. Access the server from Chrome: http://localhost:8080/fserver
or directly: http://localhost:8080/fserver/readme.txt
Ref.
https://gist.github.com/2564561
https://gist.github.com/2564562
http://www.cresc.co.jp/tech/java/Google_Dart/DartLanguageGuide.pdf (in Japanese)
Modified May 2012, by Terry Mitsuoka.
Revised July 2012 to include MIME library.
Revised Sept. 2012 to incorporate catch syntax change.
Revised Oct. 2012 to incorporate M1 changes.
Revised Feb. 2013 to incorporate newly added dart:async library.
Revised Feb. 2013 to incorporate M3 changes.
March 2013, API changes (String and HttpResponse) fixed
*/
import 'dart:io';
import 'MIME.dart' as mime;
import "dart:utf" as utf;
import 'dart:async';
final HOST = '127.0.0.1';
final PORT = 8080;
final REQUEST_PATH = "/fserver";
final LOG_REQUESTS = true;
void main() {
HttpServer.bind(HOST, PORT)
.then((HttpServer server) {
server.listen(
(HttpRequest request) {
if (request.uri.path == REQUEST_PATH) {
requestReceivedHandler(request);
}
});
print("Serving $REQUEST_PATH on http://${HOST}:${PORT}.");
});
}
void requestReceivedHandler(HttpRequest request) {
HttpResponse response = request.response;
String bodyString = ""; // request body byte data
var completer = new Completer();
if (request.method == "GET") { completer.complete("query string data received");
} else if (request.method == "POST") {
request
.transform(new StringDecoder())
.listen(
(String str){bodyString = bodyString + str;},
onDone: (){
completer.complete("body data received");},
onError: (e){
print('exeption occured : ${e.toString()}');}
);
}
else {
response.statusCode = HttpStatus.METHOD_NOT_ALLOWED;
response.close();
return;
}
// process the request
completer.future.then((data){
if (LOG_REQUESTS) {
print(createLogMessage(request, bodyString));
}
if (request.queryParameters['fileName'] != null) {
new FileHandler().onRequest(request, response);
} else
{
String fName = request.uri.path.replaceFirst(REQUEST_PATH, '');
if (fName.length > 2) {
fName = fName.substring(1);
new FileHandler().onRequest(request, response, fName);
}
else { new FileHandler().onRequest(request, response, 'Client.html');
}
}
});
}
class FileHandler {
FileHandler(){
}
void onRequest(HttpRequest request, HttpResponse response, [String fileName = null]) {
try {
if (fileName == null) {
fileName = request.queryParameters['fileName'];
}
if (LOG_REQUESTS) {
print('Requested file name : $fileName');
}
File file = new File(fileName);
if (file.existsSync()) {
String mimeType = 'text/html; charset=UTF-8';
int lastDot = fileName.lastIndexOf('.', fileName.length - 1);
if (lastDot != -1) {
String extension = fileName.substring(lastDot + 1);
mimeType = mime.mimeType(extension);
}
response.headers.set('Content-Type', mimeType);
// response.headers.set('Content-Disposition', 'attachment; filename=\'${fileName}\'');
// Get the length of the file for setting the Content-Length header.
RandomAccessFile openedFile = file.openSync();
response.contentLength = openedFile.lengthSync();
openedFile.closeSync();
// Pipe the file content into the response.
file.openRead().pipe(response);
} else {
if (LOG_REQUESTS) {
print('File not found: $fileName');
}
new NotFoundHandler().onRequest(request, response);
}
} on Exception catch (err) {
print('File Handler error : $err.toString()');
}
}
}
class NotFoundHandler {
NotFoundHandler(){
}
static final String notFoundPageHtml = '''
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL or File was not found on this server.</p>
</body></html>''';
void onRequest(HttpRequest request, HttpResponse response){
response
..statusCode = HttpStatus.NOT_FOUND
..headers.set('Content-Type', 'text/html; charset=UTF-8')
..write(notFoundPageHtml)
..close();
}
}
// create log message
StringBuffer createLogMessage(HttpRequest request, [String bodyString]) {
var sb = new StringBuffer( '''request.headers.host : ${request.headers.host}
request.headers.port : ${request.headers.port}
request.connectionInfo.localPort : ${request.connectionInfo.localPort}
request.connectionInfo.remoteHost : ${request.connectionInfo.remoteHost}
request.connectionInfo.remotePort : ${request.connectionInfo.remotePort}
request.method : ${request.method}
request.persistentConnection : ${request.persistentConnection}
request.protocolVersion : ${request.protocolVersion}
request.contentLength : ${request.contentLength}
request.uri : ${request.uri}
request.uri.path : ${request.uri.path}
request.uri.query : ${request.uri.query}
request.uri.queryParameters :
''');
request.queryParameters.forEach((key, value){
sb.write(" ${key} : ${value}\n");
});
sb.write('''request.cookies :
''');
request.cookies.forEach((value){
sb.write(" ${value.toString()}\n");
});
sb.write('''request.headers.expires : ${request.headers.expires}
request.headers :
''');
var str = request.headers.toString();
for (int i = 0; i < str.length - 1; i++){
if (str[i] == "\n") { sb.write("\n ");
} else { sb.write(str[i]);
}
}
sb.write('''\nrequest.session.id : ${request.session.id}
requset.session.isNew : ${request.session.isNew}
''');
if (request.method == "POST") {
var enctype = request.headers["content-type"];
if (enctype[0].contains("text")) {
sb.write("request body string : ${bodyString.replaceAll('+', ' ')}");
} else if (enctype[0].contains("urlencoded")) {
sb.write("request body string (URL decoded): ${urlDecode(bodyString)}");
}
}
sb.write("\n");
return sb;
}
// make safe string buffer data as HTML text
StringBuffer makeSafe(StringBuffer b) {
var s = b.toString();
b = new StringBuffer();
for (int i = 0; i < s.length; i++){
if (s[i] == '&') { b.write('&amp;');
} else if (s[i] == '"') { b.write('&quot;');
} else if (s[i] == "'") { b.write('&#39;');
} else if (s[i] == '<') { b.write('&lt;');
} else if (s[i] == '>') { b.write('&gt;');
} else { b.write(s[i]);
}
}
return b;
}
// URL decoder decodes url encoded utf-8 bytes
// Use this method to decode query string
// We need this kind of encoder and decoder with optional [encType] argument
String urlDecode(String s){
int i, p, q;
var ol = new List<int>();
for (i = 0; i < s.length; i++) {
if (s[i].codeUnitAt(0) == 0x2b) { ol.add(0x20); // convert + to space
} else if (s[i].codeUnitAt(0) == 0x25) { // convert hex bytes to a single bite
i++;
p = s[i].toUpperCase().codeUnitAt(0) - 0x30;
if (p > 9) p = p - 7;
i++;
q = s[i].toUpperCase().codeUnitAt(0) - 0x30;
if (q > 9) q = q - 7;
ol.add(p * 16 + q);
}
else { ol.add(s[i].codeUnitAt(0));
}
}
return utf.decodeUtf8(ol);
}
library MIME;
// get MIME type (returns null if there is no such extension)
String mimeType(String extension) => _mimeMaps[extension];
// default MIME type mappings
Map _mimeMaps = const {
'abs': 'audio/x-mpeg',
'ai': 'application/postscript',
'aif': 'audio/x-aiff',
'aifc': 'audio/x-aiff',
'aiff': 'audio/x-aiff',
'aim': 'application/x-aim',
'art': 'image/x-jg',
'asf': 'video/x-ms-asf',
'asx': 'video/x-ms-asf',
'au': 'audio/basic',
'avi': 'video/x-msvideo',
'avx': 'video/x-rad-screenplay',
'bcpio': 'application/x-bcpio',
'bin': 'application/octet-stream',
'bmp': 'image/bmp',
'body': 'text/html',
'cdf': 'application/x-cdf',
'cer': 'application/x-x509-ca-cert',
'class': 'application/java',
'cpio': 'application/x-cpio',
'csh': 'application/x-csh',
'css': 'text/css',
'dart': 'application/vnd.dart',
'dib': 'image/bmp',
'doc': 'application/msword',
'docm': 'application/vnd.ms-word.document.macroEnabled.12',
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dot': 'application/msword',
'dotm': 'application/vnd.ms-word.template.macroEnabled.12',
'dotx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'dtd': 'application/xml-dtd',
'dv': 'video/x-dv',
'dvi': 'application/x-dvi',
'eps': 'application/postscript',
'etx': 'text/x-setext',
'exe': 'application/octet-stream',
'gif': 'image/gif',
'gtar': 'application/x-gtar',
'gz': 'application/x-gzip',
'hdf': 'application/x-hdf',
'hqx': 'application/mac-binhex40',
'htc': 'text/x-component',
'htm': 'text/html',
'html': 'text/html',
'ico': 'image/vnd.microsoft.icon',
'ief': 'image/ief',
'jad': 'text/vnd.sun.j2me.app-descriptor',
'jar': 'application/java-archive',
'java': 'text/plain',
'jnlp': 'application/x-java-jnlp-file',
'jpe': 'image/jpeg',
'jpeg': 'image/jpeg',
'jpg': 'image/jpeg',
'js': 'text/javascript',
'jsf': 'text/plain',
'jspf': 'text/plain',
'kar': 'audio/x-midi',
'latex': 'application/x-latex',
'm3u': 'audio/x-mpegurl',
'mac': 'image/x-macpaint',
'man': 'application/x-troff-man',
'mathml': 'application/mathml+xml',
'me': 'application/x-troff-me',
'mid': 'audio/x-midi',
'midi': 'audio/x-midi',
'mif': 'application/x-mif',
'mov': 'video/quicktime',
'movie': 'video/x-sgi-movie',
'mp1': 'audio/x-mpeg',
'mp2': 'audio/x-mpeg',
'mp3': 'audio/x-mpeg',
'mp4': 'video/mp4',
'mpa': 'audio/x-mpeg',
'mpe': 'video/mpeg',
'mpeg': 'video/mpeg',
'mpega': 'audio/x-mpeg',
'mpg': 'video/mpeg',
'mpv2': 'video/mpeg2',
'ms': 'application/x-wais-source',
'nc': 'application/x-netcdf',
'oda': 'application/oda',
'odb': 'application/vnd.oasis.opendocument.database',
'odc': 'application/vnd.oasis.opendocument.chart',
'odf': 'application/vnd.oasis.opendocument.formula',
'odg': 'application/vnd.oasis.opendocument.graphics',
'odi': 'application/vnd.oasis.opendocument.image',
'odm': 'application/vnd.oasis.opendocument.text-master',
'odp': 'application/vnd.oasis.opendocument.presentation',
'ods': 'application/vnd.oasis.opendocument.spreadsheet',
'odt': 'application/vnd.oasis.opendocument.text',
'otg': 'application/vnd.oasis.opendocument.graphics-template',
'oth': 'application/vnd.oasis.opendocument.text-web',
'otp': 'application/vnd.oasis.opendocument.presentation-template',
'ots': 'application/vnd.oasis.opendocument.spreadsheet-template',
'ott': 'application/vnd.oasis.opendocument.text-template',
'ogx': 'application/ogg',
'ogv': 'video/ogg',
'oga': 'audio/ogg',
'ogg': 'audio/ogg',
'spx': 'audio/ogg',
'flac': 'audio/flac',
'anx': 'application/annodex',
'axa': 'audio/annodex',
'axv': 'video/annodex',
'xspf': 'application/xspf+xml',
'pbm': 'image/x-portable-bitmap',
'pct': 'image/pict',
'pdf': 'application/pdf',
'pgm': 'image/x-portable-graymap',
'pic': 'image/pict',
'pict': 'image/pict',
'pls': 'audio/x-scpls',
'png': 'image/png',
'pnm': 'image/x-portable-anymap',
'pnt': 'image/x-macpaint',
'ppm': 'image/x-portable-pixmap',
'ppt': 'application/vnd.ms-powerpoint',
'pot': 'application/vnd.ms-powerpoint',
'pps': 'application/vnd.ms-powerpoint',
'ppa': 'application/vnd.ms-powerpoint',
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'potx': 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx': 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'ppam': 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'pptm': 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'potm': 'application/vnd.ms-powerpoint.template.macroEnabled.12',
'ppsm': 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
'ps': 'application/postscript',
'psd': 'image/x-photoshop',
'qt': 'video/quicktime',
'qti': 'image/x-quicktime',
'qtif': 'image/x-quicktime',
'ras': 'image/x-cmu-raster',
'rdf': 'application/rdf+xml',
'rgb': 'image/x-rgb',
'rm': 'application/vnd.rn-realmedia',
'roff': 'application/x-troff',
'rtf': 'application/rtf',
'rtx': 'text/richtext',
'sh': 'application/x-sh',
'shar': 'application/x-shar',
// 'shtml': 'text/x-server-parsed-html',
'smf': 'audio/x-midi',
'sit': 'application/x-stuffit',
'snd': 'audio/basic',
'src': 'application/x-wais-source',
'sv4cpio': 'application/x-sv4cpio',
'sv4crc': 'application/x-sv4crc',
'svg': 'image/svg+xml',
'svgz': 'image/svg+xml',
'swf': 'application/x-shockwave-flash',
't': 'application/x-troff',
'tar': 'application/x-tar',
'tcl': 'application/x-tcl',
'tex': 'application/x-tex',
'texi': 'application/x-texinfo',
'texinfo': 'application/x-texinfo',
'tif': 'image/tiff',
'tiff': 'image/tiff',
'tr': 'application/x-troff',
'tsv': 'text/tab-separated-values',
'txt': 'text/plain',
'ulw': 'audio/basic',
'ustar': 'application/x-ustar',
'vxml': 'application/voicexml+xml',
'xbm': 'image/x-xbitmap',
'xht': 'application/xhtml+xml',
'xhtml': 'application/xhtml+xml',
'xls': 'application/vnd.ms-excel',
'xlt': 'application/vnd.ms-excel',
'xla': 'application/vnd.ms-excel',
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'xlsm': 'application/vnd.ms-excel.sheet.macroEnabled.12',
'xltm': 'application/vnd.ms-excel.template.macroEnabled.12',
'xlam': 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb': 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'xml': 'application/xml',
'xpm': 'image/x-xpixmap',
'xsl': 'application/xml',
'xslt': 'application/xslt+xml',
'xul': 'application/vnd.mozilla.xul+xml',
'xwd': 'image/x-xwindowdump',
'vsd': 'application/x-visio',
'war': 'application/java-archive',
'wav': 'audio/x-wav',
'wbmp': 'image/vnd.wap.wbmp',
'wml': 'text/vnd.wap.wml',
'wmlc': 'application/vnd.wap.wmlc',
'wmls': 'text/vnd.wap.wmlscript',
'wmlscriptc': 'application/vnd.wap.wmlscriptc',
'wmv': 'video/x-ms-wmv',
'wrl': 'x-world/x-vrml',
'wspolicy': 'application/wspolicy+xml',
'Z': 'application/x-compress',
'z': 'application/x-compress',
'zip': 'application/zip'
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment