Get-file web server route now support streaming (#9078)

* Update get-file.js

* Update WebServer API_ Get File.tid

* Update get-file.js

* Update get-file.js

* Update get-file.js

* Update get-file.js

* Update core-server/server/routes/get-file.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update core-server/server/routes/get-file.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update get-file.js

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
lin onetwo 2025-10-23 18:55:30 +08:00 committed by GitHub
parent 8be83cf01b
commit b061f90f87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 59 additions and 21 deletions

View file

@ -19,28 +19,55 @@ exports.info = {
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var path = require("path"), var path = require("path"),
fs = require("fs"), fs = require("fs"),
util = require("util"),
suppliedFilename = $tw.utils.decodeURIComponentSafe(state.params[0]), suppliedFilename = $tw.utils.decodeURIComponentSafe(state.params[0]),
baseFilename = path.resolve(state.boot.wikiPath,"files"), baseFilename = path.resolve(state.boot.wikiPath,"files"),
filename = path.resolve(baseFilename,suppliedFilename), filename = path.resolve(baseFilename,suppliedFilename),
extension = path.extname(filename); extension = path.extname(filename);
// Check that the filename is inside the wiki files folder // Check that the filename is inside the wiki files folder
if(path.relative(baseFilename,filename).indexOf("..") !== 0) { if(path.relative(baseFilename,filename).indexOf("..") === 0) {
// Send the file return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
fs.readFile(filename,function(err,content) { }
var status,content,type = "text/plain"; fs.stat(filename, function(err, stats) {
if(err) { if(err) {
console.log("Error accessing file " + filename + ": " + err.toString()); return state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
status = 404;
content = "File '" + suppliedFilename + "' not found";
} else { } else {
status = 200; var type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream"),
content = content; responseHeaders = {
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream"); "Content-Type": type,
"Accept-Ranges": "bytes"
};
var rangeHeader = request.headers.range,
stream;
if(rangeHeader) {
// Handle range requests
var parts = rangeHeader.replace(/bytes=/, "").split("-"),
start = parseInt(parts[0], 10),
end = parts[1] ? parseInt(parts[1], 10) : stats.size - 1;
// Validate start and end
if(isNaN(start) || isNaN(end) || start < 0 || end < start || end >= stats.size) {
responseHeaders["Content-Range"] = "bytes */" + stats.size;
return response.writeHead(416, responseHeaders).end();
}
var chunksize = (end - start) + 1;
responseHeaders["Content-Range"] = "bytes " + start + "-" + end + "/" + stats.size;
responseHeaders["Content-Length"] = chunksize;
response.writeHead(206, responseHeaders);
stream = fs.createReadStream(filename, {start: start, end: end});
} else {
responseHeaders["Content-Length"] = stats.size;
response.writeHead(200, responseHeaders);
stream = fs.createReadStream(filename);
}
// Common stream error handling
stream.on("error", function(err) {
if(!response.headersSent) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.end("Read error");
} else {
response.destroy();
} }
state.sendResponse(status,{"Content-Type": type},content);
}); });
} else { stream.pipe(response);
state.sendResponse(404,{"Content-Type": "text/plain"},"File '" + suppliedFilename + "' not found");
} }
});
}; };

View file

@ -1,5 +1,5 @@
created: 20181002123907518 created: 20181002123907518
modified: 20181002124345482 modified: 20250605000000000
tags: [[WebServer API]] tags: [[WebServer API]]
title: WebServer API: Get File title: WebServer API: Get File
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -15,11 +15,22 @@ Parameters:
* ''pathname'' - URI encoded path to the file * ''pathname'' - URI encoded path to the file
Headers:
* ''Range'' - <<.from-version "5.3.7">> (optional) Request specific byte ranges using the format `bytes=start-end`. Supports partial content delivery for media streaming.
Response: Response:
* 200 OK * 200 OK
*> `Content-Type: <content-type>` (determined from file extension) *> `Content-Type: <content-type>` (determined from file extension)
*> Body: data retrieved from file *> `Content-Length: <file-size>`
*> `Accept-Ranges: bytes`
*> Body: complete file data
* 206 Partial Content (when Range header is provided)
*> `Content-Type: <content-type>` (determined from file extension)
*> `Content-Length: <range-size>`
*> `Content-Range: bytes <start>-<end>/<total-size>`
*> `Accept-Ranges: bytes`
*> Body: requested byte range data
* 403 Forbidden * 403 Forbidden
* 404 Not Found * 404 Not Found