File Management System
Author(s)
- Ashik
Last Updated Date
2025-10-24
SRS References
- 3.4.1 File Upload
- 3.4.2 File Retrieval
- 3.4.3 Access Control
Version History
| Version | Date | Changes | Author |
|---|---|---|---|
| 1.0 | 2025-10-24 | Initial draft for File Management module | Ashik |
Feature Overview
Objective:
Implement a centralized file management system for handling files uploads and retrievals with type-based organization, automatic filename generation.
Scope:
- Binary file upload with type classification (Inventory, Dealer)
- Support for image formats (.jpg, .jpeg, .png, .svg) and documents (.xlsx)
- Type-specific folder storage (
/uploads/inventory/,/uploads/dealer/) - Default root folder storage (
/uploads/) for unrecognized types - Unique filename generation (GUID/timestamp-based)
- File metadata tracking in database
- Integration with inventorymaster table
- File retrieval through existing Inventory Fetch API
Dependencies:
- IAM Service (authentication)
- Storage provider (local filesystem or S3)
- inventorymaster table
- Email service for notifications (optional)
Requirements
Functional Requirements
-
File Upload
- Accept file uploads with required Enum type parameter (Inventory/Dealer)
- Store files in type-specific folders (
/uploads/inventory/,/uploads/dealer/) - If Enum type doesn't match predefined types, store in root folder (
/uploads/) - Generate unique filenames to prevent collisions
- Record full file path in filelocation field
- Insert metadata into filemetadata table
-
Entity Integration
- Update inventorymaster.thumbnailimage with generatedfilename (single image)
- Append to inventorymaster.images field (comma-separated for multiple images)
- Support file-entity mapping through foreign keys
- Maintain referential integrity between tables
-
File Retrieval
- Integrate with existing Inventory Fetch API
- Return both generated filename and full path
- Support JOIN queries between inventorymaster and filemetadata
- If Enum type doesn't match (Inventory/Dealer), fetch from root folder (
/uploads/) - Exclude soft-deleted files from results
-
File Management
- Soft-delete files (mark as deleted, retain physical file)
- Retrieve file metadata by ID
- Validate Enum type values
Non-Functional Requirements
-
Security
- Validate file types (whitelist: .jpg, .jpeg, .png, .svg, .xlsx)
- Enforce file size limits
- Never expose internal filesystem paths
- Implement role-based access control
-
Performance
- Optimize JOIN queries with proper indexing
- API response time under 500ms
- Efficient pagination support
-
Scalability
- Support cloud storage migration (S3, CDN)
- Handle large volumes of files
- Extensible for additional Enum types
-
Reliability
- Audit trail with timestamps
- Data consistency across tables
- Prevent orphaned records
Design Specifications
UI/UX Design
(Dealer Portal / Admin Panel)
- File upload component with type selector dropdown (Inventory/Dealer)
- Drag-and-drop or click-to-browse interface
- Image preview after successful upload
- Inline error messages for validation failures
- Progress indicator during upload
Workflow
Upload Process:
- Client selects file and type (Inventory/Dealer)
- Backend validates file type and size
- Generate unique filename (GUID-based)
- Determine storage path:
- If type = "Inventory" →
/uploads/inventory/ - If type = "Dealer" →
/uploads/dealer/ - Default/Other →
/uploads/(root folder)
- If type = "Inventory" →
- Insert record into filemetadata table with full path
- Return file metadata to client
Retrieval Process (Inventory Master Example):
- Inventory Fetch API called
- Query inventorymaster table
- LEFT JOIN with filemetadata for thumbnailimage
- Split images field by comma to get all image filenames
- Query filemetadata for each image filename
- Determine file path based on enumtype:
- If enumtype = "Inventory" →
/uploads/inventory/{filename} - If enumtype = "Dealer" →
/uploads/dealer/{filename} - Default/Other →
/uploads/{filename}(root folder)
- If enumtype = "Inventory" →
- Build imagepaths array with full file locations
- Return inventory data with thumbnail and multiple image paths
- Filter out soft-deleted files
Data Models
public class FileMetadata
{
public Guid Id { get; set; }
public string OriginalFileName { get; set; }
public string GeneratedFileName { get; set; }
public string FileLocation { get; set; }
public string EnumType { get; set; } // "Inventory" or "Dealer"
public DateTime CreatedAt { get; set; }
public bool IsDeleted { get; set; }
}
public class InventoryMaster
{
public Guid Id { get; set; }
public string ThumbnailImage { get; set; } // Single thumbnail - References filemetadata.generatedfilename
public string Images { get; set; } // Multiple images - Comma-separated generatedfilenames (e.g., "img1.jpg,img2.jpg,img3.jpg")
// ... other inventory fields
}
public record FileUploadRequest
{
[Required]
public List<IFormFile> Files { get; init; } // Accepts single or multiple files
[Required]
public FileType Type { get; init; } // Inventory or Dealer
}
public record FileUploadResponse
{
public List<UploadedFileInfo> UploadedFiles { get; init; }
public int TotalUploaded { get; init; }
public string UpdatedImagesField { get; init; } // Only populated if InventoryId provided
}
public record UploadedFileInfo
{
public Guid Id { get; init; }
public string OriginalFileName { get; init; }
public string GeneratedFileName { get; init; }
public string FileLocation { get; init; }
public string EnumType { get; init; }
public DateTime CreatedAt { get; init; }
}
public record InventoryDetailsWithImage
{
public Guid Id { get; init; }
public string ThumbnailImage { get; init; }
public string ThumbnailImagePath { get; init; }
public string Images { get; init; } // Comma-separated filenames
public List<string> ImagePaths { get; init; } // Full paths for all images
// ... other inventory fields
}
public enum FileType
{
Inventory,
Dealer
}
Database Schema:
-- filemetadata table
CREATE TABLE filemetadata (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
originalfilename VARCHAR(255),
generatedfilename VARCHAR(255) NOT NULL UNIQUE,
filelocation VARCHAR(500) NOT NULL,
enumtype VARCHAR(50) NOT NULL, -- Stores 'Inventory', 'Dealer', or other types (falls back to root folder)
createdat TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
isdeleted BOOLEAN DEFAULT FALSE
);
CREATE INDEX idx_filemetadata_generatedfilename ON filemetadata(generatedfilename);
CREATE INDEX idx_filemetadata_enumtype ON filemetadata(enumtype);
-- inventorymaster table (Partial)
CREATE TABLE inventorymaster (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
thumbnailimage VARCHAR(255), -- Single thumbnail image
images TEXT, -- Comma-separated list of image filenames
-- ... other columns
FOREIGN KEY (thumbnailimage) REFERENCES filemetadata(generatedfilename)
);
CREATE INDEX idx_inventorymaster_thumbnailimage ON inventorymaster(thumbnailimage);
-- Note: images field contains comma-separated generatedfilenames
-- Example: "img1.jpg,img2.jpg,img3.jpg"
-- Each filename should exist in filemetadata.generatedfilename
API Interfaces
| Endpoint | Method | Payload | Response | Status Codes |
|---|---|---|---|---|
/api/v1/files/upload | POST | files (IFormFile/IFormFile[]), type | FileUploadResponse | 201, 400, 415, 500 |
/api/v1/files/delete | DELETE | fileNames (array in body) | CommonResponse | 200, 404, 500 |
/api/v1/files/{fileId} | GET | fileId | FileMetadata | 200, 404, 500 |
/api/v1/files/download/{generatedFileName} | GET | generatedFileName | File (attachment) | 200, 404, 500 |
API Request/Response Examples
Upload File (Single or Multiple)
Request (Single File):
POST /api/v1/files/upload
Content-Type: multipart/form-data
files: <binary>
type: "Inventory"
Response (201):
{
"uploadedFiles": [
{
"id": "a3f4b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c",
"originalFileName": "car_front.jpg",
"generatedFileName": "a3f4b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c.jpg",
"fileLocation": "/uploads/inventory/a3f4b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c.jpg",
"enumType": "Inventory",
"createdAt": "2025-10-24T10:15:00Z"
}
],
"totalUploaded": 1,
"updatedImagesField": "img1.jpg,a3f4b2c1.jpg"
}
Request (Multiple Files):
POST /api/v1/files/upload
Content-Type: multipart/form-data
files: <binary[]>
type: "Inventory"
Response (201):
{
"uploadedFiles": [
{
"id": "a3f4b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c",
"originalFileName": "car_front.jpg",
"generatedFileName": "a3f4b2c1.jpg",
"fileLocation": "/uploads/inventory/a3f4b2c1.jpg",
"enumType": "Inventory",
"createdAt": "2025-10-24T10:15:00Z"
},
{
"id": "b4e5c3d2-6f7e-5g8h-9i0j-1k2l3m4n5o6p",
"originalFileName": "car_side.jpg",
"generatedFileName": "b4e5c3d2.jpg",
"fileLocation": "/uploads/inventory/b4e5c3d2.jpg",
"enumType": "Inventory",
"createdAt": "2025-10-24T10:15:00Z"
}
],
"totalUploaded": 2,
"updatedImagesField": "img1.jpg,img2.jpg,a3f4b2c1.jpg,b4e5c3d2.jpg"
}
Note:
filesparameter accepts both single file and multiple files- Response format is consistent for both single and multiple uploads