DEV Community

Cover image for MEAN Stack Project: Create Quick File Share Application
Varun
Varun

Posted on

MEAN Stack Project: Create Quick File Share Application

Hey, remember that day when you wanted to share that video or different format file but WhatsApp would reduce its quality or wouldn't support that format. Well we all have faced similar situations when we wanted to share file with our friend or family folder to cousins. But now we are going to make an application where we can directly send Any Type of File to any user and he can download it with just a link.

So here we are going to use Angular 14 for Frontend, NodeJS for Backend, and MongoDB Atlas as Backend and we can even Host this project.

so here I am giving Project Repo to go to if you face any problem
just clone it in your folder by writing this in terminal
git clone repolink
Frontend Repo: https://github.com/varun21vaidya/ShareNow
Backend Repo: https://github.com/varun21vaidya/ShareNow-Backend

Lets First Decide Features we want to build:

  1. User story: I can drag and drop an image to upload it
  2. User story: I can choose to select an image from my folder
  3. User story: When the image is uploaded, It shows that and Provides a Link to Download
  4. User story: I can choose to copy the Link to the clipboard
  5. User story: Download Page with Encoded File Name but Accurate File size to confirm our File
  6. User Story: Download button directly downloads the required File

ANGULAR Part:

ng new frontend

Then we have to create a project structure:
which includes

  1. component 'upload' which will be homepage
  2. component 'completed' which will be shown when we upload file
  3. 'fileupload' service which will be used to upload file through backend api and get download link from backend.

Image description

ng g c upload
ng g c completed
ng g s fileupload
ng g s sendurl
Enter fullscreen mode Exit fullscreen mode

We will also use bootstrap:
so put this in index.html

for header:

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">

Enter fullscreen mode Exit fullscreen mode
<body>
  <app-root></app-root>
  <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"></script>
</body>
Enter fullscreen mode Exit fullscreen mode

app.module will look like this:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UploadComponent } from './upload/upload.component';
import { HttpClientModule } from '@angular/common/http';
import { CompletedComponent } from './completed/completed.component';
@NgModule({
  declarations: [
    AppComponent,
    UploadComponent,
    CompletedComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Enter fullscreen mode Exit fullscreen mode

Now we also need to decide the routes in app-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CompletedComponent } from './completed/completed.component';
import { UploadComponent } from './upload/upload.component';

const routes: Routes = [
  { path: 'uploaded', component: CompletedComponent },
  { path: '', component: UploadComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
Enter fullscreen mode Exit fullscreen mode

Now we will make global styles.css:

/* You can add global styles to this file, and also import other style files */
html,
body {
  height: 100%;
  background-color: rgb(189, 255, 255);
}
Enter fullscreen mode Exit fullscreen mode

Now Create View for a home page component in upload.component.html

<div class="container">
    <div class="upload-container">
        <div class="headline py-3 px-5 mb-2 bg-primary text-white">
            <h5>Share Your Files Superquickly</h5>
        </div>
        <div class="uploader">
            <h3>Upload Your File</h3>
            <!-- <p>File should be jpeg,png..</p> -->
            <div class="drag-container">

                <div class="dropdiv" [class.processing]="processing" (dragover)="onDrag($event)"
                    (drop)="onDrop($event)">
                    <input type="file" (change)="onChange($event)" style="display:none">
                    <img src="../../assets/files.jpg" alt="icon" class="files rounded mb-3 d-block">
                    <p class="msg mx-2">Drag and Drop your File here</p>
                </div>
            </div>
            <p>Or</p>

            <div class="upload">
                <input type="file" name="uploadfile" id="img" style="display:none;" (change)="selectFile($event)" />
                <label for="img" class="btn btn-primary">Choose a file</label>
            </div>

            <div class="col-2">
                <button class="btn btn-success btn-sm" [hidden]="!selectedFiles" (click)="upload()">
                    Upload
                </button>
            </div>


            <div *ngIf="errmsg" class="alert alert-secondary" role="alert">{{ errmsg }}</div>
        </div>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

css file:

.container,
.drag-container,
.upload-container,.uploader {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.container {
  height: 100vh;
}

.uploader {
  width: 350px;
  height: 420px;
  justify-content: space-evenly;
  border: none;
}

.upload-container{
  border-radius: 10px;
  background-color: white;
  box-shadow: 1px 2px 20px 0px rgb(0 0 0 / 20%);
}

.drag-container {
  box-sizing: border-box;
  background: #f6f8fb;
  border: 2px dashed #97bef49c;
  border-radius: 12px;
}

.files {
  width: 225px;
  height: 130px;
}

Enter fullscreen mode Exit fullscreen mode

Upload Component ts file

Now we have to create two methods, first using choose file from folders and second to drag and drop.

now for drag and drop, use event binding
(dragover)="onDrag($event)" (drop)="onDrop($event)"
which detects a change when a file is dragged into the input section.
while choose file uses (change)="selectFile($event)"

and on submit button upload method is called, which uses upload method from fileupload service to upload file to the server.
The function first checks if file is selected or not and then subscribes to the service method which returns the event and checks the progress with event.type ===HttpEventType.UploadProgress and when it completes it navigates to completed component.

import { HttpEventType, HttpResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { FileuploadService } from '../services/fileupload.service';
import { Router } from '@angular/router';
import { SendurlService } from '../services/sendurl.service';

@Component({
  selector: 'app-upload',
  templateUrl: './upload.component.html',
  styleUrls: ['./upload.component.css'],
})
export class UploadComponent implements OnInit {
  selectedFiles?: FileList;
  currentFile?: File;
  progress = 0;
  message = '';
  errmsg = '';

  fileInfos?: Observable<any>;

  constructor(
    private uploadService: FileuploadService,
    private router: Router,
    private sendurl: SendurlService
  ) {}

  ngOnInit(): void {}

  // drag and drop files
  processing?: boolean;

  onDrag(event: any) {
    event.preventDefault();
  }

  onDrop(event: any) {
    event.preventDefault();

    this.onFileChange(event.dataTransfer.files);
  }

  onChange(event: any) {
    this.onFileChange(event.target.files);
  }

  private onFileChange(files: any) {
    this.processing = true;
    this.selectedFiles = files;
    setTimeout(() => {
      console.log('processed');
      this.processing = false;
    }, 1000);
  }

  // select file
  selectFile(event: any): void {
    this.selectedFiles = event.target.files;
  }
  upload(): void {
    this.progress = 0;

    if (this.selectedFiles) {
      const file: File | null = this.selectedFiles.item(0);
      console.log('file has been selected');
      if (file) {
        this.currentFile = file;

        this.uploadService.upload(this.currentFile).subscribe({
          next: (event: any) => {
            if (event.type === HttpEventType.UploadProgress) {
              this.progress = Math.round((100 * event.loaded) / event.total);
              console.log(this.progress);
              // console.log(event.loaded);
            } else if (event instanceof HttpResponse) {
              console.log('this is file link', event.body.file);
              this.message = event.body.file;

              // this service will send the download link from the server
              // to child component of upload complete
              this.sendurl.link = this.message;
              this.router.navigate(['/', 'uploaded']);
            }
          },
          error: (err: any) => {
            console.log(err);
            this.progress = 0;

            if (err.error && err.error.message) {
              this.errmsg = err.error.message;
            } else {
              this.errmsg = 'Could not upload the file!';
            }

            this.currentFile = undefined;
          },
        });
      }

      this.selectedFiles = undefined;
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

So now your webapp would look like this:
Image description

in sendurl service add this:
link?:string;

Now create upload service:

The service takes file and returns an observable httpevent which uses POST Request to send the file to server. Also create getfiles method to get the link.

import { Injectable } from '@angular/core';
import { HttpClient, HttpRequest, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class FileuploadService {
  // base url
  // host = 'https://share-now-backend.vercel.app/';
  host = 'https://sharenow.onrender.com/';
  // host = 'http://localhost:3000/';
  uploadURL = `${this.host}api/files`;

  constructor(private http: HttpClient) {}

  upload(file: File): Observable<HttpEvent<any>> {
    const formData: FormData = new FormData();
    formData.append('myfile', file);

    const req = new HttpRequest('POST', `${this.uploadURL}`, formData, {
      reportProgress: true,
      responseType: 'json',
    });

    return this.http.request(req);
  }

  getFiles(): Observable<any> {
    return this.http.get(`${this.uploadURL}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we need to have a component when we upload file successfully, and it should provide the link to download.

completed html:

<div class="container">
    <div class="upload-container">

        <h4 class="mt-2"> <img src="../../assets/checkmark.png" alt="checkmark"> Uploaded Successfully</h4>
        <br>
        <img src="../../assets/completed.jpg" id="showimg" alt="image">
        <br>
        <div class="card-body">
            <div class="input-group">
                <input type="text" value={{link}} #userinput>
                <button type="button" class="btn btn-primary" (click)="copyInputMessage(userinput)">Copy
                    Link</button>
            </div>
        </div>
        <button class="btn btn-success" [routerLink]="['/']">Home</button>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

completed css:

.container,
.drag-container,
.upload-container {
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  align-items: center;
}

.container {
  height: 100vh;
}

.upload-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border: 1px solid black;
  padding: 20px 15px;
  width: 350px;
  height: 500px;
  border: none;
  box-shadow: 1px 2px 20px 0px rgb(0 0 0 / 20%);
  border-radius: 10px;
  background-color: white;
}

#showimg {
  width: 330px;
  height: auto;
  border-radius: 8px;
}

Enter fullscreen mode Exit fullscreen mode

Now to show download link we need to get it from sendurl service

completed component ts file:

import { Component, OnInit, Input } from '@angular/core';
import { SendurlService } from '../services/sendurl.service';

@Component({
  selector: 'app-completed',
  templateUrl: './completed.component.html',
  styleUrls: ['./completed.component.css'],
})
export class CompletedComponent implements OnInit {
  constructor(private geturl: SendurlService) {}
  link = this.geturl.link;
  ngOnInit(): void {}
  /* To copy Text from Textbox */
  copyInputMessage(inputElement: any) {
    inputElement.select();
    document.execCommand('copy');
    inputElement.setSelectionRange(0, 0);
  }
}

Enter fullscreen mode Exit fullscreen mode

This will show the file upload completed view with the link to download the file.
Image description

This Completes our Angular App !! Hope you Enjoyed working on this app, and learned something new.

Further We will have to make Backend APIs with NodeJS and Express.
Here is Part 2 where we will complete our FULL STACK APP:
https://dev.to/varun21vaidya/mean-stack-project-create-quick-file-share-application-part-2-backend-22c3

HAPPY CODING !!

Top comments (0)