Exclude file that not fully transferred - node.js

I am writing a typescript function to display all the files in the current directory and all the subfolders. It works like a charm but wanted to exclude the file that is still under transfer(growing file). The transfer is done via a 3rd part tool, and I can't access the source to compare.
Here is the code
interface DirectoryFiles {
fileName: string;
directory: string;
}
private async ListFilesDir(
currDirecory: string,
fileArray: DirectoryFiles[] | [],
): Promise<DirectoryFiles[]> {
const allFiles = await this.getAllFilesInDir(currDirecory);
fileArray = fileArray || [];
for (const file of allFiles) {
if (fs.statSync(currDirecory + '/' + file).isDirectory()) {
fileArray = await this.ListFilesDir(
join(currDirecory, file),
fileArray,
);
} else {
fileArray.push({
directory: currDirecory,
fileName: file,
});
}
}
return fileArray;
}
async getAllFilesInDir(dir: string): Promise<string[]> {
try {
return fsPromises.readdir(dir);
} catch (error) {
throw new Error(`Error occurred while reading directory ${dir}!`);
}
}
been figuring out this for a while now. Thank you for your time.

Related

Node JS uploading file field as a field of an object in the request body using Formidable

I am building a Node JS application using Express JS. I need to implement the file upload logic for my application. I am using Formidable, https://www.npmjs.com/package/formidable for uploading file(s). I could upload the files using that library without any issue when the request body format is simple. Now, I am trying to upload the file which is a property/ field of an object in the request body. The following is the dummy request body structure.
{
users: [
{
name: `Test User 1`,
photoFile: {{ here I want to upload file for this user }}
},
{
name: `Test User 2`,
photoFile: {{ here I want to upload file for this user }}
},
// the list goes on
]
}
I am trying to send the test payload/ data in the Postman as in the screenshot below.
This is my code to parse the form
private getForm = async (
req: Request,
options: FormOptions = {
multiples: false
}
) => {
const tempUploadDir = this.getTempUploadDirectory(req);
if (!(await this.storage.exits(tempUploadDir))) {
await this.storage.mkdir(tempUploadDir);
}
return new IncomingForm({
multiples: options.multiples,
uploadDir: tempUploadDir,
maxFileSize: config.fileSystem.maxUploadLimit
});
};
public parseFormData = async <T>(
request: Request,
options: FormOptions = {
multiples: false
}
): Promise<T> => {
const form = await this.getForm(request, options);
return new Promise<T>((resolve) => {
form.parse(request, (err, fields, files) => {
if (err) {
if (err.code === FILE_TOO_LARGE_ERROR_CODE) {
// file too large
throw new UploadMaxFileSizeException(
`Upload file size limit exceeded.`
);
}
throw err;
}
let filesData: {
[key: string]: IUploadedFile[];
} = {};
for (let fileField in files) {
if (`length` in files[fileField]) {
filesData[fileField] = files[fileField] as IUploadedFile[];
} else {
filesData[fileField] = [files[fileField] as IUploadedFile];
}
}
return resolve({
...fields,
...files
} as T);
});
});
};
When I print out the result of parseFormData, I am getting the following result.
As you can see, the field field, 'users[0][photoFile]' is not parsed putting into the corresponding field of the request body object. Instead, the entire field name is string, 'users[0][photoFile]'. What is wrong with my code and how can I fix it?
I my project, file will upload to server when user upload files and get the link back
And when submit form, user avatar just is a link, not a file

Cypress is returning an empty array when trying to log sheetnames of an excel file

I am currently trying to get the sheetnames of an excel file but Cypress is returning an empty array. Is there something I missed? I'll be using it to verify data on later steps.
I'm using Cypress 9.6.0 with Cucumber. Below are my scripts and screenshots:
index.js for task
module.exports = (on, config) => {
on('file:preprocessor', cucumber());
on('task', {
checkExcelSheetContents(args){
if (fs.existsSync(args.filePath)) {
const workbook = xlsx.readFile(args.filePath);
return xlsx.utils.sheet_to_json(workbook.SheetNames)
} else {
throw new Error ("File not found")
}
}
})
return Object.assign({}, config, {
fixturesFolder: 'cypress/fixtures',
integrationFolder: 'cypress/integration',
screenshotsFolder: 'cypress/screenshots',
videosFolder: 'cypress/videos',
supportFile: 'cypress/support/index.js'
});
}
.js file
And ('try', () => {
var excelFilePath = "../CreateAutomatedTests/cypress/downloads/courses20220714_09_51_27.xlsx"
cy.wrap(excelFilePath).as('filePath')
cy.get('#filePath').then((filePath) => {
cy.task('checkExcelSheetContents', { filePath }).then((contents) => {
cy.log(contents)
})
})
})
Please see these screenshots as well
I've always used the buffer version of xlsx.read().
From xlsx package
For Node ESM, the readFile helper is not enabled. Instead, fs.readFileSync should be used to read the file data as a Buffer for use with XLSX.read:
import { readFileSync } from "fs";
import { read } from "xlsx/xlsx.mjs";
const buf = readFileSync("test.xlsx");
/* buf is a Buffer */
const workbook = read(buf);
Your task:
on('task', {
checkExcelSheetContents(args){
if (fs.existsSync(args.filePath)) {
const buf = fs.readFileSync(file);
const workbook = xlsx.read(buf, { type: 'buffer' });
return workbook.SheetNames
} else {
throw new Error ("File not found")
}
}
})

Refactor Typescript class into two functions

I have a project in Node JS and Typescript in which I have a file with a class that I use to check that a directory exists and create another. To the method I pass two parameters, the main directory ('src/out') and a string with which to create the following directory ('api').
This is my class, the dir variable ('src / out') and the api variable ('api'):
export class createDir {
checkExistsOrCreate(dir: string, api: any) {
let dire = `${dir}${api}`;
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
if (fs.existsSync(dire)) {
rimraf.sync(dire);
fs.mkdirSync(dire);
}else {
fs.mkdirSync(dire);
}
return dire;
}
}
What I want to do is create two functions: the first one I pass the main directory ('src / out') and it checks if it exists. And the second creates the directories, if the principal does not exist, it creates the principal directory and the api directory ('src / out / api') and if the principal exists it will only create the '/ api' directory.
My problem is that I don't know how to separate both functions and how to tell the second if the main directory exists or not.
This is the first function that only checks that the directory that reaches it exists:
export class exists {
exitsDir(dir: string) {
if (!fs.existsSync(dir)){
return false;
}else{
return true;
}
}
}
This is the class from which I call the directory check and pass the directory to check:
class UtilsController {
public async utilsDir () {
try {
let checkDir = new exists();
await checkDir.exitsDir('src/out');
} catch (error) {
console.log(error);
}
}
}
Actually the mkdir function can handle your use case automatically when using the recursive option.
const fs = require('fs');
fs.mkdirSync('main/sub', {recursive: true});
There are a lot of classes that are not really needed but this should work:
class UtilsController {
public utilsDir(): Promise<boolean> {
try {
const checkDir = new exists();
return checkDir.exitsDir('src/out');
} catch (error) {
console.log(error);
}
}
}
const test = async () => {
const utilsController = new UtilsController();
const exists = await utilsController.utilsDir();
console.log(exists);
};

copying files using `ncp` throws: no such file or directory, mkdir

I'm using ncp to copy files as following:
import ncp from "ncp";
import { promisify } from "util";
const ncpPromise = promisify(ncp);
const copyAssets = async (exportFolderName, includeSourceMaps) => {
const assets = glob.sync("**/", { cwd: distPath });
const options = { clobber: true, stopOnErr: true };
if (!includeSourceMaps) {
options.filter = (f) => {
return !f.endsWith(".map");
};
}
return Promise.all(
assets.map((asset) => {
return ncpPromise(
path.join(distPath, asset),
path.join(exportPath, exportFolderName, asset),
options
);
})
);
};
But this sometimes fails with the following error:
"ENOENT: no such file or directory, mkdir '/path/to/folder'"
How can I solve this ?
I guess you are trying to copy all files matching for the given glob, so you need to do:
const assets = glob.sync("**/*.*", { cwd: distPath }); // note the *.*
For example, your current glob in question will result into:
[
'folder1/',
'folder2/',
]
whereas the glob in this answer will result into (This is what you want):
[
'folder1/file1.txt',
'folder1/file2.txt',
'folder2/anotherfile.txt',
]
An Alternative:
Seems like ncp isn't being maintained. So, you can use fs-extra, it can copy file and directory as well:
const glob = require("glob");
const path = require("path");
const fs = require("fs-extra");
const copyAssets = async (exportFolderName, includeSourceMaps) => {
const assets = glob.sync("**/*.*", { cwd: distPath });
const options = { overwrite: true };
if (!includeSourceMaps) {
options.filter = (f) => {
return !f.endsWith(".map");
};
}
return Promise.all(
assets.map((asset) => {
return fs
.copy(
path.join(distPath, asset),
path.join(exportPath, exportFolderName, asset),
options
)
.catch((e) => console.log(e));
})
);
};
NPM qir (yes, it is published by myself) is another choice:
const qir = require('qir');
qir.asyncing.copy('/A/path/to/src', '/B/path/to/dest')
.then(() => { /* OK */ }
.catch(ex => { /* Something wrong */ }
;
Here, /A/path/to/src may be a file or a folder, and /B/path/to is not required to exist already.
There is a synchronous way:
const qir = require('qir');
qir.syncing.copy('/A/path/to/src', '/B/path/to/dest');
And, if both src and dest located in the same directory:
const qir = require('qir');
let q = new qir.AsyncDir('/current/working/dir');
q.copy('A/path/to/src', 'B/path/to/dest')
.then(() => { /* OK */ }
.catch(ex => { /* Something wrong */ }
;
It will copy /current/working/dir/A/path/to/src to /current/working/dir/B/path/to/dest.

hapijs - serve static files from directory handler with variable path name

I'm trying to do something like this in my routes file using hapijs + inert plugin
{
method: 'GET',
path: '/l/{path*}',
handler: {
directory: {
path: (req) => {
const defaultDir = '/public';
return getHostInfo(req.headers.host).then((d) => {
if (!d || !d.directory) return defaultDir;
return d.directory;
}).catch((e) => {
return defaultDir;
});
}
}
}
},
path parameter expects a string, array or function which returns a string or array...in my case, my function returns a promise...so it doesn't work.
I tried adding hapi-as-promised package which modifies reply function to support then method but did't work.
Basically I want to serve static assets from one directory or another depending on the host header value.
Well, the only thing i have in my mind is this hack. First, you have to make an extension point to make your async stuff:
server.ext({
type: `onPreHandler`,
method: (request, reply) => {
const defaultDir = '/public';
return getHostInfo(request.headers.host).then((d) => {
if (!d || !d.directory) {
request.app.dirPath = defaultDir;
return;
}
request.app.dirPath = d.directory;
}).catch((e) => {
request.app.dirPath = defaultDir;
}).then(() => {
return reply.continue();
});
}
});
And then the route:
{
method: `get`,
path: `/l/{path*}`,
handler: {
directory: {
path: (request) => {
return request.app.dirPath;
}
}
}
}
I don't know how right is this way, but i tested and it worked. So i hope this helps. And i gotta notice, using node to server files on production isn't a common way by some reason.

Resources