WIP: Support for streaming multipart form data to the file system

This commit is contained in:
Jeremy Ruston 2024-02-16 16:02:40 +00:00
parent 59b425fd5c
commit 8edefffbc5
2 changed files with 107 additions and 7 deletions

View File

@ -34,14 +34,19 @@ exports.startup = function() {
logger.alert("The plugin 'tiddlywiki/multiwikiserver' requires the better-sqlite3 npm package to be installed. Run 'npm install' in the root of the TiddlyWiki repository");
return;
}
// Compute the database path
var databasePath = path.resolve($tw.boot.wikiPath,"store/database.sqlite");
// Create and initialise the tiddler store
var SqlTiddlerStore = require("$:/plugins/tiddlywiki/multiwikiserver/sql-tiddler-store.js").SqlTiddlerStore;
// Create and initialise the tiddler store and upload manager
var SqlTiddlerStore = require("$:/plugins/tiddlywiki/multiwikiserver/sql-tiddler-store.js").SqlTiddlerStore,
store = new SqlTiddlerStore({
databasePath: path.resolve($tw.boot.wikiPath,"store/database.sqlite")
}),
UploadManager = require("$:/plugins/tiddlywiki/multiwikiserver/upload-manager.js").UploadManager,
uploadManager = new UploadManager({
inboxPath: path.resolve($tw.boot.wikiPath,"store/inbox"),
store: store
});
$tw.mws = {
store: new SqlTiddlerStore({
databasePath: databasePath
})
store: store,
uploadManager: uploadManager
};
// Create docs bag and recipe
$tw.mws.store.createBag("docs","TiddlyWiki Documentation from https://tiddlywiki.com/");

View File

@ -0,0 +1,95 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/upload-manager.js
type: application/javascript
module-type: library
A class that handles an incoming multipart/form-data stream, streaming the data to temporary files
in the store/inbox folder. It invokes a callback when all the data is available. The callback can explicitly
claim some or all of the files, otherwise they are deleted on return from the callback. Claimed files should
be moved out of the store/inbox folder.
\*/
(function() {
/*
Create an instance of the upload manager. Options include:
inboxPath - path to the inbox folder
store - sqlTiddlerStore to use for saving tiddlers
*/
function UploadManager(options) {
const path = require("path");
options = options || {};
this.inboxPath = path.resolve(options.inboxPath,);
this.store = options.store;
}
/*
Process a new multipart/form-data stream. Options include:
state - provided by server.js
recipe - optional name of recipe to write to (one of recipe or bag must be specified)
bag - optional name of bag to write to (one of recipe or bag must be specified)
callback - invoked as callback(err,results). Results is an array of {title:,bag_name:}
formData is:
{
parts: [
{
name: "fieldname",
filename: "filename",
filePath: "/users/home/mywiki/store/inbox/09cabc74-8163-4ead-a35b-4ca768f02d62/64131628-cbff-4677-b146-d85c42c232dc",
headers: {
name: "value",
...
}
},
...
]
}
*/
UploadManager.prototype.processNewStream = function(options) {
let fileStream = null;
let fieldValue = "";
state.streamMultipartData({
cbPartStart: function(headers,name,filename) {
console.log(`Received file ${name} and ${filename} with ${JSON.stringify(headers)}`)
if(filename) {
fileStream = fs.createWriteStream(filename);
} else {
fieldValue = "";
}
},
cbPartChunk: function(chunk) {
if(fileStream) {
fileStream.write(chunk);
} else {
fieldValue = fieldValue + chunk;
}
},
cbPartEnd: function() {
if(fileStream) {
fileStream.end();
fileStream = null;
} else {
console.log("Data was " + fieldValue);
fieldValue = "";
}
},
cbFinished: function(err) {
if(err) {
state.sendResponse(400,{"Content-Type": "text/plain"},"Bad Request: " + err);
} else {
state.sendResponse(200, {"Content-Type": "text/plain"},"Multipart data processed");
}
}
});
}
UploadManager.prototype.close = function() {
};
exports.UploadManager = UploadManager;
})();