211 lines
6.7 KiB
TypeScript
211 lines
6.7 KiB
TypeScript
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;
|
|
}
|