22-Nodejs Course 2023: UploadedFile
Tags: node, typescript, mongodb, fastify
Published: 2022-11-01 15:55:53

---
title: 22-Nodejs Course 2023: UploadedFile
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
#cover_image: 
# Use a ratio of 100:42 for best results.
---

We have made such an impressive progress in our request handler, now let's continue doing our amazing work.

## UploadedFile

We will start by creating a new file in the `src/core/http` folder called `UploadedFile.ts`

```ts
// src/core/http/UploadedFile.ts

export default class UploadedFile {
  constructor(
    private readonly fileData: any,
  ) {}
}
```

We created a simple class which will receive the file object (that contains the file data).

Now what should we do with this class? well let's list its features

## Uploaded File Features

- Get file name.
- Get file size.
- Get file mimetype.
- Get file extension.
- Save file to a specific path.
- Save file to a specific path with a specific name.
- Save file to a specific path with random generated name.

Pretty cool right? let's start implementing them.

## Request File

Now let's update our `Request`'s method `parseInputValue` to be something like this

```ts
// src/core/http/Request.ts

  /**
   * Parse the given data
   */
  private parseInputValue(data: any) {
    if (data.file) {
      return data;
    }

    // data.value appears only in the multipart form data
    // if it json, then just return the data
    if (data.value !== undefined) return data.value;

    return data;
  }
```

Here we w'll make a check if there is a `file` object in the data, then we'll return the data as it is, otherwise we'll return the `data.value` which is the value of the input.

The last return statement will be used with json data (not multipart form data).

Now let's create `file` method.

```ts
// src/core/http/request.ts
  /**
   * Get Uploaded file instance
   */
  public file(key: string): UploadedFile | null {
    const file = this.input(key);

    return file ? new UploadedFile(file) : null;
  }
```

Here we got the normal file content from the input method, if exists, then return it in a new instance of `UploadedFile` class, otherwise return null.

The data that is being taken from the `multipart` are too many, but we need only the file name, mime type and the file buffer content, these what we'll take from  the `file` object.

```ts
// src/core/http/UploadedFile.ts
import path from "path";

export default class UploadedFile {
  /**
   * Constructor
   */
  constructor(private readonly fileData: any) {}

  /**
   * Get File name
   */
  public get name(): string {
    return this.fileData.filename;
  }

  /**
   * Get File mime type
   */
  public get mimeType(): string {
    return this.fileData.mimetype;
  }

  /**
   * Get file extension
   */
  public get extension(): string {
    return path.extname(this.name).replace(".", "");
  }
}
```

The only thing that i can point to here is the `extension` method, we used `path.extname` to get the extension of the file, then we removed the dot from the extension.

But let's make it in lower case anyways to make sure nothing fishy happens.

```ts
// src/core/http/UploadedFile.ts
  /**
   * Get file extension
   */
  public get extension(): string {
    return path.extname(this.name).replace(".", "").toLowerCase();
  }
```

## Saving File

Now let's go to the important part, which is saving the file, let's create a method called `saveTo` in our `UploadedFile` class, this method will receive the directory that we'll store inside it our file.

```ts
import { ensureDirectory } from "@mongez/fs";
import { writeFileSync } from "fs";
import path from "path";

export default class UploadedFile {
  /**
   * Constructor
   */
  constructor(private readonly fileData: any) {}

  /**
   * Get File name
   */
  public get name(): string {
    return this.fileData.filename;
  }

  /**
   * Get File mime type
   */
  public get mimeType(): string {
    return this.fileData.mimetype;
  }

  /**
   * Get file extension
   */
  public get extension(): string {
    return path.extname(this.name).replace(".", "").toLowerCase();
  }

  /**
   * Save the file to the given path
   */
  public async saveTo(path: string) {
    const file = await this.buffer();

    ensureDirectory(path);

    writeFileSync(path + "/" + this.name, file);
  }

  /**
   * Get file buffer
   */
  public buffer() {
    return this.fileData.toBuffer();
  }
}
```

We used `ensureDirectory` to make sure that the directory exists, then we used `writeFileSync` to write the file to the given path.

## Save File With Specific Name

Now let's create a method called `saveAs` which will receive the path and the name of the file.

```ts
// src/core/http/UploadedFile.ts
  /**
   * Save the file to the given path with the given name
   */
  public async saveAs(path: string, name: string) {
    const file = await this.buffer();

    ensureDirectory(path);

    writeFileSync(path + "/" + name, file);
  }
```

Now let's go to our `create-user.ts` file and give it a try.

```ts
// src/app/users/controllers/create-user.ts
import { rootPath } from "@mongez/node";
import { Request } from "core/http/request";

export default async function createUser(request: Request) {
  const image = request.file("image");

  if (image) {
    await image.saveTo(rootPath("storage"));
  }

  return {
    done: true,
  };
}
```

The `rootPath` is a function that gives you the path of the root directory of the project, we can pass to it any internal path to generate a full path, we used it to get the path of the `storage` folder.

Now open postman and make a post request to `http://localhost:3000/users` and select body type as `form-data` and add a key called `image` and select a file, then send the request.

## File Size

Now let's add a method called `size` which will return the size of the file in bytes.

Please note that this method will be a `async` method as we'll call the `buffer` method which is also an `async` method.

So we're going to use the buffer in two scenarios, one is to get the size of the file, and the other is to save the file, so we'll use the buffer to get the size of the file and save it in a variable, then we'll use this variable to save the file.

Let's make a variable called `fileBuffer` and assign it to the buffer of the file.

```ts
// src/core/http/UploadedFile.ts
import { ensureDirectory } from "@mongez/fs";
import { writeFileSync } from "fs";
import path from "path";

export default class UploadedFile {
  /**
   * File Buffer
   */
  private fileBuffer: any;

  /**
   * Constructor
   */
  constructor(private readonly fileData: any) {}

  /**
   * Get File name
   */
  public get name(): string {
    return this.fileData.filename;
  }

  /**
   * Get File mime type
   */
  public get mimeType(): string {
    return this.fileData.mimetype;
  }

  /**
   * Get file extension
   */
  public get extension(): string {
    return path.extname(this.name).replace(".", "").toLowerCase();
  }

  /**
   * Save the file to the given path
   */
  public async saveTo(path: string) {
    const file = await this.buffer();

    ensureDirectory(path);

    writeFileSync(path + "/" + this.name, file);
  }

  /**
   * Save the file to the given path with the given name
   */
  public async saveAs(path: string, name: string) {
    const file = await this.buffer();

    ensureDirectory(path);

    writeFileSync(path + "/" + name, file);
  }

  /**
   * Get file size in bytes
   */
  public async size() {
    const fileBuffer = await this.buffer();

    return fileBuffer.toString().length;
  }

  /**
   * Get file buffer
   */
  public async buffer() {
    if (this.fileBuffer) {
      return this.fileBuffer;
    }

    return (this.fileBuffer = await this.fileData.toBuffer());
  }
}
```

## Random generated file name

Now let's add our last method which is `save`, this method will generate a random name for the file and save it to the given path.

Now let's install another small package which we'll use later a lot, [Reinforcements](https://github.com/hassanzohdy/reinforcements).

```bash
yarn add @mongez/reinforcements
```

```ts
// src/core/http/UploadedFile.ts
import { ensureDirectory } from "@mongez/fs";
import { Random } from "@mongez/reinforcements";
import { writeFileSync } from "fs";
import path from "path";

export default class UploadedFile {
  /**
   * File Buffer
   */
  private fileBuffer: any;

  /**
   * Constructor
   */
  constructor(private readonly fileData: any) {}

  /**
   * Get File name
   */
  public get name(): string {
    return this.fileData.filename;
  }

  /**
   * Get File mime type
   */
  public get mimeType(): string {
    return this.fileData.mimetype;
  }

  /**
   * Get file extension
   */
  public get extension(): string {
    return path.extname(this.name).replace(".", "").toLowerCase();
  }

  /**
   * Save the file to the given path
   */
  public async saveTo(path: string) {
    const file = await this.buffer();

    ensureDirectory(path);

    writeFileSync(path + "/" + this.name, file);
  }

  /**
   * Save the file to the given path with the given name
   */
  public async saveAs(path: string, name: string) {
    const file = await this.buffer();

    ensureDirectory(path);

    writeFileSync(path + "/" + name, file);
  }

  /**
   * Save the file to the given path with random name
   */
  public async save(path: string) {
    const file = await this.buffer();

    ensureDirectory(path);

    const name = Random.string(64) + "." + this.extension;

    writeFileSync(path + "/" + name, file);

    return name;
  }

  /**
   * Get file size in bytes
   */
  public async size() {
    const fileBuffer = await this.buffer();

    return fileBuffer.toString().length;
  }

  /**
   * Get file buffer
   */
  public async buffer() {
    if (this.fileBuffer) {
      return this.fileBuffer;
    }

    return (this.fileBuffer = await this.fileData.toBuffer());
  }
}
```

Now let's give it another test in our controller

```ts
// src/app/users/controllers/create-user.ts
import { rootPath } from "@mongez/node";
import { Request } from "core/http/request";

export default async function createUser(request: Request) {
  const image = request.file("image");

  let name = "";

  if (image) {
    name = await image.save(rootPath("storage"));
  }

  return {
    image: {
      name,
      size: await image?.size(),
    },
  };
}
```

And that's it!

## Conclusion

In this tutorial, we've learned how to upload files using multer and how to save them to the disk, we've also learned how to get the file size and how to generate a random name for the file.

In our next article, we'll make it more configurable.

## 🎨 Project Repository

You can find the latest updates of this project on [Github](https://github.com/hassanzohdy/nodejs-2023)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) to get help and support (Node Js 2023 Channel).

## 🎞️ Video Course (Arabic Voice)

If you want to learn this course in video format, you can find it on [Youtube](https://www.youtube.com/playlist?list=PLGO8ntvxgiZMJc7RN2lIq9WmMOlWZGzmz), the course is in Arabic language.

## 💰 Bonus Content 💰

You may have a look at these articles, it will definitely boost your knowledge and productivity.

General Topics

- [Event Driven Architecture: A Practical Guide in Javascript](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
- [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
- [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

- [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
- [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
- [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

 React Js Packages

- [useFetcher: easiest way to fetch data in React Js](https://dev.to/hassanzohdy/usefetcher-the-easiest-way-to-fetch-data-in-react-45o9)

Courses (Articles)

- [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)