diff --git a/.gitignore b/.gitignore index 9154f4c..2ff1fa2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,9 @@ -# ---> Java -# Compiled class file +### Java +apiPdfUploader/target/ + +# files *.class - -# Log file *.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # *.jar *.war *.nar @@ -20,7 +12,35 @@ *.tar.gz *.rar -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* +#Eclipse Specific +.metadata/ +bin/ +tmp/ +*.tmp +*.bak +*.swp +local.properties +.settings/ +.loadpath +.recommenders +.project +.classpath +.factorypath + +### NodeJS +webPdfUploader/node_modules/ +webPdfUploader/build/ +webPdfUploader/package-lock.json + +# VSCode +.vscode/ + + +### OS Specific +.DS_Store +Thumbs.db + + +### Other +fullstackPdfUploader/apiPdfUploader/storage diff --git a/apiPdfUploader/pom.xml b/apiPdfUploader/pom.xml new file mode 100644 index 0000000..cbfdab6 --- /dev/null +++ b/apiPdfUploader/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.3.4 + + + + com.example + apiPdfUploader + 1.0.0 + apiPdfFileManager + Sample Spring Boot REST API for storing/serving PDF files with MSSQL metadata + + + 21 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.microsoft.sqlserver + mssql-jdbc + runtime + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/apiPdfUploader/schema-mssql.sql b/apiPdfUploader/schema-mssql.sql new file mode 100644 index 0000000..8ff8824 --- /dev/null +++ b/apiPdfUploader/schema-mssql.sql @@ -0,0 +1,26 @@ +-- Reference schema for MS SQL Server. +-- Not required to run manually if spring.jpa.hibernate.ddl-auto=update is left enabled, +-- but useful for real deployments where DDL is managed separately. + +IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'pdf_file_db') +BEGIN + CREATE DATABASE pdf_file_db; +END +GO + +USE pdf_file_db; +GO + +IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'file_document') +BEGIN + CREATE TABLE file_document ( + id BIGINT IDENTITY(1,1) PRIMARY KEY, + original_file_name NVARCHAR(255) NOT NULL, + stored_file_name NVARCHAR(255) NOT NULL, + file_path NVARCHAR(1000) NOT NULL, + content_type NVARCHAR(100) NULL, + file_size BIGINT NOT NULL DEFAULT 0, + upload_date DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME() + ); +END +GO diff --git a/apiPdfUploader/src/main/java/com/example/pdfservice/PdfServiceApplication.java b/apiPdfUploader/src/main/java/com/example/pdfservice/PdfServiceApplication.java new file mode 100644 index 0000000..772909e --- /dev/null +++ b/apiPdfUploader/src/main/java/com/example/pdfservice/PdfServiceApplication.java @@ -0,0 +1,15 @@ +package com.example.pdfservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; + +@SpringBootApplication +@ConfigurationPropertiesScan +public class PdfServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(PdfServiceApplication.class, args); + } + +} diff --git a/apiPdfUploader/src/main/java/com/example/pdfservice/config/FileStorageProperties.java b/apiPdfUploader/src/main/java/com/example/pdfservice/config/FileStorageProperties.java new file mode 100644 index 0000000..668a1af --- /dev/null +++ b/apiPdfUploader/src/main/java/com/example/pdfservice/config/FileStorageProperties.java @@ -0,0 +1,22 @@ +package com.example.pdfservice.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Reads "app.file.storage-dir" from application.properties. + * This is the ROOT directory on disk where uploaded PDFs are physically kept. + * The full path of each individual file is what gets persisted in the DB. + */ +@ConfigurationProperties(prefix = "app.file") +public class FileStorageProperties { + + private String storageDir; + + public String getStorageDir() { + return storageDir; + } + + public void setStorageDir(String storageDir) { + this.storageDir = storageDir; + } +} diff --git a/apiPdfUploader/src/main/java/com/example/pdfservice/config/WebConfig.java b/apiPdfUploader/src/main/java/com/example/pdfservice/config/WebConfig.java new file mode 100644 index 0000000..7af319c --- /dev/null +++ b/apiPdfUploader/src/main/java/com/example/pdfservice/config/WebConfig.java @@ -0,0 +1,21 @@ +package com.example.pdfservice.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Allows the React dev server (http://localhost:3000) to call this API + * during local development. Tighten this for production deployments. + */ +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/api/**") + .allowedOrigins("http://localhost:3000") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*"); + } +} diff --git a/apiPdfUploader/src/main/java/com/example/pdfservice/controller/FileController.java b/apiPdfUploader/src/main/java/com/example/pdfservice/controller/FileController.java new file mode 100644 index 0000000..3b06562 --- /dev/null +++ b/apiPdfUploader/src/main/java/com/example/pdfservice/controller/FileController.java @@ -0,0 +1,76 @@ +package com.example.pdfservice.controller; + +import com.example.pdfservice.dto.FileResponse; +import com.example.pdfservice.entity.FileDocument; +import com.example.pdfservice.service.FileStorageService; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; + +@RestController +@RequestMapping("/api/files") +public class FileController { + + private final FileStorageService fileStorageService; + + public FileController(FileStorageService fileStorageService) { + this.fileStorageService = fileStorageService; + } + + /** 6. List all files */ + @GetMapping + public ResponseEntity> listFiles() { + return ResponseEntity.ok(fileStorageService.listAll()); + } + + /** 7. Upload a new PDF file (multipart/form-data, field name "file") */ + @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) { + FileResponse response = fileStorageService.store(file); + return ResponseEntity.ok(response); + } + + /** 7. Download a file as an attachment */ + @GetMapping("/{id}/download") + public ResponseEntity downloadFile(@PathVariable Long id) { + FileDocument metadata = fileStorageService.getMetadata(id); + Resource resource = fileStorageService.loadAsResource(id); + + String encodedName = URLEncoder.encode(metadata.getOriginalFileName(), StandardCharsets.UTF_8); + + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_PDF) + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + metadata.getOriginalFileName() + "\"; filename*=UTF-8''" + encodedName) + .body(resource); + } + + /** 8. Preview a file inline in the browser (e.g. inside an