when i try ti implement code like that it works: gateway.service.ts
{
path: "/api",
whitelist: [ "**"],
use: [],
mergeParams: true,
authentication: true,
authorization: true,
autoAliases: true,
aliases: {
"login": "auth.login",
},
bodyParsers: {
json: {
strict: false,
limit: "1MB",
},
urlencoded: {
extended: true,
limit: "1MB",
},
},
mappingPolicy: "all", // Available values: "all", "restrict"
// Enable/disable logging
logging: true,
},
{
path: "/controller",
bodyParsers: {
json: false,
urlencoded: false
},
aliases: {
"POST /upload": {
params: {
file: { type: "file", required: true },
checkList: { type: "string", default:'' }
},
type: "multipart",
busboyConfig: {
limits: {
files: 3
}
},
action: "controller.post"
}
},
busboyConfig: {
limits: {
files: 3
}
},
mappingPolicy: "restrict"
}
but when i separte every rooting for the specific service it not works : like that:
gateway.service.ts
{
path: "/api",
whitelist: [ "**"],
use: [],
mergeParams: true,
authentication: true,
authorization: true,
autoAliases: true,
aliases: {
"login": "auth.login",
},
bodyParsers: {
json: {
strict: false,
limit: "1MB",
},
urlencoded: {
extended: true,
limit: "1MB",
},
},
mappingPolicy: "all", // Available values: "all", "restrict"
// Enable/disable logging
logging: true,
},
controller.service.ts // here it not works but i don't know why
settings:{
routes:[
{
path: "/controller",
bodyParsers: {
json: false,
urlencoded: false
},
aliases: {
"POST /upload": {
params: {
file: { type: "file", required: true },
checkList: { type: "string", default:'' }
},
type: "multipart",
busboyConfig: {
limits: {
files: 3
}
},
action: "controller.post"
}
},
busboyConfig: {
limits: {
files: 3
}
},
mappingPolicy: "restrict"
}
]
}
i tried to impelement rooting and aliases of every service in it x.service.ts file it not works when i put every rooting settings in it file , it works only when i put all the settings in the gateway.service.ts file
try this trick you can make a class that
import Busboy from "@fastify/busboy";
import _ from "lodash";
class FileUploaderHandler {
static async handle($ctx: any, route: any, req: any, res: any): Promise<any> {
return new Promise((resolve, reject) => {
const ctx = req.$ctx;
const files: any = [];
let promises: any = [];
const busboyConfig = { limits: {} };
const busboyOptions = _.defaultsDeep(
{
// very important : it pass the header from coming request to busboy
// it need the usage of symbol cause header key is a symbol in the request
headers: req[Object.getOwnPropertySymbols(req)[1]],
},
busboyConfig,
busboyConfig,
);
const busboy = new Busboy(busboyOptions);
(<any>ctx.meta).$multipart = {};
let numOfFiles = 0;
let hasField = false;
busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
let parsedFile = {
fieldname: fieldname,
filename: filename,
encoding: encoding,
mimetype: mimetype,
file: file,
actionName: ctx.params.req.$action.name,
};
files[files.length] = parsedFile;
// send file to the service
promises.push(
ctx
.call(
ctx.params.req.$action.name.split(".")[0] + ".upload",
file,
_.defaultsDeep({}, route.opts.callOptions, {
meta: {
file: parsedFile,
},
}),
)
.catch((err: any) => {
file.resume();
}),
);
// if we indicate file size limit per file in config
file.on("limit", () => {
if (_.isFunction(busboyOptions.onFileSizeLimit)) {
busboyOptions.onFileSizeLimit.call(ctx.service, file, busboy);
}
});
numOfFiles++;
// it must be called else "finish" not be invoked
});
//if there is a normal field (string, etc ..)
busboy.on("field", (field, value) => {
hasField = true;
ctx.meta.$multipart[field] = value;
});
busboy.on("finish", async () => {
await ctx.service.Promise.all(promises);
resolve(files);
});
busboy.on("error", (err: any) => {
req.unpipe(req.busboy);
req.resume();
});
// if there is error to be thrown by busboy
["partsLimit", "filesLimit", "fieldsLimit"].forEach((event) => {
if (_.isFunction(busboyOptions[`on${_.capitalize(event)}`])) {
busboy.on(event, () =>
busboyOptions[`on${_.capitalize(event)}`].call(
ctx.service,
busboy,
this,
ctx.service,
),
);
}
});
req.pipe(busboy);
});
}
}
export default FileUploaderHandler;`
then you can call it in your onBeforeCall in your gateway :
async onBeforeCall(
ctx: Context<unknown, Meta>,
route: Route,
req: IncomingRequest,
res: GatewayResponse,
): Promise<void> {
// Set request headers to context meta
ctx.meta.userAgent = req.headers["user-agent"];
// check if there are multipart action
if (req.$action.isMultipart) { // to avoid regression
const files = await FileUploaderHandler.handle(ctx, route, req, res);
(<any>ctx.meta).files = files;
}
}
in your service you can use it like that :
import: {
rest: {
method: "POST",
path: "/import",
},
isMultipart: true, // you should add it here
handler(ctx: any): any {
const directoryFile = ctx.meta.files.find(
(file: any) => file.fieldname == "name_of_field_here",
);
if (directoryFile) {
return {
message: "file uploaded successfully",
files: ctx.meta.files,
};
}
},
},
upload: {
handler(ctx: any): any {
return new Promise((resolve, reject) => {
if (typeof ctx.params.pipe == "function") {
// file upload
const startTime = Date.now();
const f = fs.createWriteStream(
path.join("upload/", ctx.meta.file.filename),
);
f.on("close", () => {
resolve({});
});
ctx.params.pipe(f);
} else {
reject(new Error("NO_FILES"));
}
});
},
},