Intro
This time, I will try searching local files and returning them for getting requests.
Environemtns
- Node.js ver.17.0.1
- TypeScript ver.4.4.4
- Express ver.4.17.1
- ts-node ver.10.4.0
Directories & files
I want to get files from these directories.
sample_files
L type1
L subFolder
L こんにちは.txt
L webrtc_image.pptx
L type2
L sub folder
L こんにちは.txt
L Hello world!.txt
Get directory and file informations
I can get directory and file informations by "fs.promises.readdir".
fileLoader.ts
import fs from "fs";
async function getFilePath(): Promise<void> {
const directory = "C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files";
// get directory or file informations as fs.Dirent[]
const paths = await fs.promises.readdir(directory, { withFileTypes: true});
// get only file names as string[]
// const paths = await fs.promises.readdir(directory);
for(const p of paths) {
console.log(`Path ${p.name} Dir:${p.isDirectory()}`);
}
}
Result
Path Hello world!.txt Dir:false
Path type1 Dir:true
Path type2 Dir:true
Search sub directories
"fs.promises.readdir" can't get from sub directories.
So I have to do by my self.
fileLoader.ts
import fs from "fs";
import path from "path/posix";
const rootDirectory = "C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files";
export async function loadFile(): Promise<void> {
const gotPath = await getFilePath(rootDirectory);
console.log(gotPath);
}
async function getFilePath(directory: string): Promise<string|null> {
// get directory or file informations as fs.Dirent[]
const paths = await fs.promises.readdir(directory, { withFileTypes: true});
for(const p of paths) {
if(p.isDirectory()) {
console.log(`Folder ${p.name} Dir:${directory}`);
await getFilePath(path.join(directory, p.name));
} else {
console.log(`File ${p.name} Dir:${directory}`);
}
}
}
Results
File Hello world!.txt Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files
Folder type1 Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files
Folder subFolder Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files/type1
File こんにちは.txt Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files/type1/subFolder
File webrtc_image.pptx Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files/type1
Folder type2 Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files
Folder sub folder Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files/type2
File こんにちは.txt Dir:C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files/type2/sub folder
And search specified files by name.
fileLoader.ts
...
export async function loadFile(): Promise<void> {
const gotPath = await getFilePath(rootDirectory, "webrtc_image.pptx");
console.log(gotPath);
}
async function getFilePath(directory: string, fileName: string): Promise<string|null> {
// get directory or file informations as fs.Dirent[]
const paths = await fs.promises.readdir(directory, { withFileTypes: true});
for(const p of paths) {
if(p.isDirectory()) {
const filePath = await getFilePath(path.join(directory, p.name), fileName);
if(filePath != null) {
return filePath;
}
} else {
if(p.name === fileName) {
return path.join(directory, p.name);
}
}
}
return null;
}
Results
C:/Users/example/OneDrive/Documents/workspace/express-sample/sample_files/type1/webrtc_image.pptx
- File system - Node.js v17.0.1 documentation
- Node.js fs.readdir recursive directory search - Stack Overflow
Load files or create error messages
Next, I will get files from the gotten file paths and return the data.
But if I won't be able to find the files, I want to return error messages as JSON files.
file.type.ts
export type DownloadFile = {
name: string,
mimeType: "application/json"|"application/vnd.openxmlformats-officedocument.presentationml.presentation"|"text/plain",
fileData: Buffer,
};
fileLoader.ts
import fs from "fs";
import path from "path/posix";
import { DownloadFile } from "./file.type";
...
export async function loadFile(fileName: string): Promise<DownloadFile> {
const gotPath = await getFilePath(rootDirectory, fileName);
if(gotPath == null) {
return getFailedMessage(`File: ${fileName} was not found`);
}
const extension = getMimeType(path.extname(gotPath));
if(extension == null) {
return getFailedMessage(`Unknown type File: ${fileName}`);
}
const fileData = await fs.promises.readFile(gotPath);
return {
name: fileName,
mimeType: extension,
fileData,
};
}
...
function getFailedMessage(errorMessage: string): DownloadFile {
const failedResult = {
succeeded: false,
errorMessage
};
const fileData = Buffer.from(JSON.stringify(failedResult));
return {
name: "failedResult.json",
mimeType: "application/json",
fileData,
};
}
function getMimeType(value: string): "application/json"|
"application/vnd.openxmlformats-officedocument.presentationml.presentation"|
"text/plain"|
null {
switch(value) {
case ".json":
return "application/json";
case ".pptx":
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
case ".txt":
return "text/plain";
}
return null;
}
- Common MIME types - HTTP | MDN
- JavaScript: The Definitive Guide, 7th Edition
- [Express][TypeScript] Downloading files
Return file data
I will set loaded or generated file data as response value.
index.ts
import express from "express";
import { loadFile } from "./files/fileLoader";
const port = 3098;
const app = express();
app.get('/files', async (req, res) => {
// TODO: get file names from URL paramters
const result = await loadFile("webrtc_image.pptx");
res.setHeader("File-Name", result.name);
res.setHeader("Content-Type", result.mimeType);
res.write(result.fileData);
res.end();
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
Set multi byte text into Response Headers
One problem is I can't set multi byte text into Response Headers.
Or I will get an exception.
TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["File-Name"]
at ServerResponse.setHeader (node:_http_outgoing:579:3)
at ServerResponse.header (C:\Users\example\OneDrive\Documents\workspace\express-sample\node_modules\express\lib\response.js:771:10)
at C:\Users\example\OneDrive\Documents\workspace\express-sample\index.ts:15:9
at step (C:\Users\example\OneDrive\Documents\workspace\express-sample\index.ts:33:23)
at Object.next (C:\Users\example\OneDrive\Documents\workspace\express-sample\index.ts:14:53)
at fulfilled (C:\Users\example\OneDrive\Documents\workspace\express-sample\index.ts:5:58) {
code: 'ERR_INVALID_CHAR'
}
So I encode the file name.
index.ts
...
app.get('/files', async (req, res) => {
// TODO: get file names from URL paramters
const result = await loadFile("こんにちは.txt");
res.setHeader("File-Name", encodeURI(result.name));
res.setHeader("Content-Type", result.mimeType);
res.write(result.fileData);
res.end();
});
...
Top comments (0)