import {Component, ElementRef, ViewChild} from '@angular/core'; import {DecimalPipe, NgClass, NgIf} from "@angular/common"; import {FormatFileSizePipePipe} from "../format-file-size-pipe.pipe"; import {FormsModule} from "@angular/forms"; import axios, {AxiosProgressEvent} from "axios"; import { DevelopmentStore } from '../../store/DevelopmentStore'; import {RouterLink} from "@angular/router"; @Component({ selector: 'app-upload', standalone: true, imports: [ NgIf, FormatFileSizePipePipe, FormsModule, DecimalPipe, NgClass, RouterLink ], templateUrl: './upload.component.html', styleUrl: './upload.component.scss' }) export class UploadComponent { @ViewChild('fileInput') fileInput!: ElementRef; fileToUpload: File | null = null; shortStorage: boolean = false; singleDownload: boolean = false; fileDescription: string = ''; passwordProtected: boolean = false; uploadStarted: boolean = false; uploadProgress = 0; // Real progress targetUploadProgress = 0; // New target progress to reach uploadFinished = false; uploadSpeedBps: number = 0; uploadData: FileDetails | null = null; fileUrls: { downloadUrl: string, statusUrl: string, deleteUrl: string } | null = null; urlCopied: boolean = false; constructor(private developmentStore: DevelopmentStore) { this.uploadSpeedTest(); } /** * Manages the file input click or drag and drop */ onFileSelected(event: any): void { const files = event.target.files; if (files && files.length > 0) { this.handleFiles(files); } } onDragOver(event: DragEvent): void { event.preventDefault(); event.stopPropagation(); // Optionally add visual feedback } onDrop(event: DragEvent): void { event.preventDefault(); event.stopPropagation(); const files = event.dataTransfer?.files; if (files && files.length > 0) { this.handleFiles(files); } // Remove visual feedback } handleFiles(files: FileList): void { for(let i = 0; i < files.length; i++) { const file = files[i]; this.fileToUpload = file; } this.calculateEstimatedUploadTime(); } startUpload(): void { if (this.fileToUpload) { console.log(`Uploading file: ${this.fileToUpload.name}, Short storage: ${this.shortStorage}, Single download: ${this.singleDownload}, Password protected: ${this.passwordProtected}`); this.uploadStarted = true; const formData = this.buildFormDataObject(); const config = { headers: { 'Content-Type': 'multipart/form-data', 'Access-Control-Allow-Origin': '*', // Allow CORS }, onUploadProgress: (progressEvent: AxiosProgressEvent) => { if (progressEvent.total) { this.targetUploadProgress = Math.round((progressEvent.loaded / progressEvent.total) * 100); } else { this.uploadProgress = 99; // Unable to compute progress since total size is unknown console.error('Unable to compute progress since total size is unknown'); } this.smoothProgressUpdate(); } }; axios.post(this.developmentStore.getBaseUrl() + 'api/v1/upload', formData, config) .then(response => { console.log('Upload completed successfully!'); console.log(response.data); this.fileUrls = this.buildFileUrls(response.data); this.uploadData = response.data; this.uploadFinished = true; }) .catch(error => { console.error('Upload failed:', error.response ? error.response.data : error.message); }); } } buildFileUrls(fileDetails: FileDetails) { const baseUrl = this.developmentStore.getBaseUrl(); const fileId = fileDetails.fileId; const downloadUrl = `${baseUrl}download?fileId=${fileId}`; const deleteUrl = `${baseUrl}api/v1/deletefile?fileId=${fileId}`; const statusUrl = `${baseUrl}status?fileId=${fileId}`; return { downloadUrl, statusUrl, deleteUrl, }; } buildFormDataObject(): FormData { const formData = new FormData(); formData.append('file', this.fileToUpload as Blob); formData.append('passwordProtected', this.passwordProtected ? 'true' : 'false'); formData.append('singleDownload', this.singleDownload.toString()); formData.append('shortStorage', this.shortStorage.toString()); formData.append('fileDescription', this.fileDescription); console.log(formData.get("passwordProtected")); return formData; } private uploadSpeedTest() { const start = new Date().getTime(); // Start timer // Create a 1MB payload for upload const payload = new ArrayBuffer(1024 * 1024); // 1MB const uint8View = new Uint8Array(payload); for (let i = 0; i < uint8View.length; i++) { uint8View[i] = 0; } axios({ method: 'post', url: this.developmentStore.getBaseUrl() + 'api/v1/upload-speed-test', data: uint8View, headers: { 'Content-Type': 'application/octet-stream', 'Access-Control-Allow-Origin': '*', // Allow CORS }, onUploadProgress: function(progressEvent) { // Optional: handle progress events for feedback } }) .then(response => { const end = new Date().getTime(); // End timer const duration = (end - start) / 1000; // Convert ms to seconds const bitsUploaded = payload.byteLength * 8; this.uploadSpeedBps = bitsUploaded / duration; console.log("Speed test ping finished."); }) .catch(error => { console.error('Error during upload test:', error); }); } private calculateEstimatedUploadTime() : void { const fileSize = this.fileToUpload?.size; const uploadSpeed = this.uploadSpeedBps / 8; // Convert bits per second to bytes per second if (fileSize && uploadSpeed) { const estimatedTimeInSeconds = fileSize / uploadSpeed; console.log(`Estimated upload time: ${estimatedTimeInSeconds} seconds`); } } smoothProgressUpdate() { if (this.uploadProgress < this.targetUploadProgress) { this.uploadProgress += 0.01 * (this.targetUploadProgress - this.uploadProgress); requestAnimationFrame(this.smoothProgressUpdate.bind(this)); } else if (this.uploadProgress > this.targetUploadProgress) { // Handle overshoot this.uploadProgress = this.targetUploadProgress; } } copyUrlToClipboard(url: string | undefined) { if(url) { navigator.clipboard.writeText(url).then(() => { this.urlCopied = true; console.log('Text successfully copied to clipboard'); }).catch(err => { console.error('Failed to copy text to clipboard', err); }); } } } interface FileDetails { fileId: string; fileName: string; message: string; passwordProtected: boolean; singleDownload: boolean; uploadDate: string; password: string; }