From e9259f9a76275ff7a69268804dc98ab14dea7477 Mon Sep 17 00:00:00 2001 From: gsd Date: Thu, 17 Apr 2025 22:45:54 +0300 Subject: [PATCH] files uploader --- .../java/app/controllers/FileController.java | 85 +++++++++++++++++++ src/main/java/app/entities/db/DbFile.java | 39 +++++++++ .../app/repositories/FilePSRepository.java | 15 ++++ .../java/app/repositories/FileRepository.java | 9 ++ src/main/resources/application.yaml | 4 + 5 files changed, 152 insertions(+) create mode 100644 src/main/java/app/controllers/FileController.java create mode 100644 src/main/java/app/entities/db/DbFile.java create mode 100644 src/main/java/app/repositories/FilePSRepository.java create mode 100644 src/main/java/app/repositories/FileRepository.java diff --git a/src/main/java/app/controllers/FileController.java b/src/main/java/app/controllers/FileController.java new file mode 100644 index 0000000..c8f2f0e --- /dev/null +++ b/src/main/java/app/controllers/FileController.java @@ -0,0 +1,85 @@ +package app.controllers; + +import app.annotations.enums.CollectStages; +import app.annotations.interfaces.*; +import app.entities.SearchFilter; +import app.entities.db.DbFile; +import app.repositories.FilePSRepository; +import app.repositories.FileRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +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.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.UUID; + +@RestController +@RequestMapping("api/files") +public class FileController { + + @Autowired + private FileRepository fileRepository; + + @Autowired + private FilePSRepository filePSRepository; + + @PostMapping + @CheckWebAccess + @CheckPermitionFlag(flag = "z") + @CollectStatistic(stage = CollectStages.COMBINED) + public ResponseEntity upload(@CookieValue(value = "steam64") String steam64, + @RequestParam("file") MultipartFile multipartFile) throws IOException { + if (multipartFile.isEmpty() || multipartFile.getSize() == 0L) return ResponseEntity.noContent().build(); + + UUID uuid = UUID.randomUUID(); + Timestamp timestamp = Timestamp.from(Instant.now()); + + DbFile dbFile = new DbFile(); + dbFile.setUploader(steam64); + dbFile.setFilename(multipartFile.getOriginalFilename()); + dbFile.setFilesize(multipartFile.getSize()); + dbFile.setData(multipartFile.getBytes()); + dbFile.setTimestamp(timestamp); + dbFile.setId(uuid); + fileRepository.save(dbFile); + return ResponseEntity.ok(uuid.toString()); + } + + @GetMapping("/{uuid}") + public ResponseEntity download(@PathVariable String uuid) throws UnsupportedEncodingException { + DbFile dbFile = fileRepository.findById(UUID.fromString(uuid)).orElse(null); + if (dbFile == null) return ResponseEntity.notFound().build(); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(dbFile.getFilename())); + return ResponseEntity.ok() + .headers(headers) + .contentLength(dbFile.getFilesize()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(new InputStreamResource(new ByteArrayInputStream(dbFile.getData()))); + } + + @PostMapping("/search") + @CheckWebAccess + @CheckPermitionFlag(flag = "z") + @CollectStatistic(stage = CollectStages.COMBINED) + public Page getFiles(Pageable pageable, @RequestBody(required = false) SearchFilter searchFilter) { + return filePSRepository.getFiles(pageable); + } + + private static String encodeFileName(String fileName) throws UnsupportedEncodingException { + return URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20"); + } +} diff --git a/src/main/java/app/entities/db/DbFile.java b/src/main/java/app/entities/db/DbFile.java new file mode 100644 index 0000000..3364976 --- /dev/null +++ b/src/main/java/app/entities/db/DbFile.java @@ -0,0 +1,39 @@ +package app.entities.db; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; +import lombok.Data; + +import java.sql.Timestamp; +import java.util.UUID; + +@Entity +@Table(schema = "tf2_facti13", name = "files") +@Data +public class DbFile { + @Id + @Column(name = "id") + private UUID id; + + @Column(name = "uploader") + private String uploader; + + @Column(name = "filename", length = 255) + private String filename; + + @Column(name = "filesize") + private Long filesize; + + @Column(name = "extrainfo") + private String extraInfo; + + @Column(name = "data", columnDefinition = "bytea") + @JsonIgnore + private byte[] data; + + @Column(name = "ts") + private Timestamp timestamp; + + @Column(name = "deleted") + private Boolean deleted; +} diff --git a/src/main/java/app/repositories/FilePSRepository.java b/src/main/java/app/repositories/FilePSRepository.java new file mode 100644 index 0000000..98ff5d8 --- /dev/null +++ b/src/main/java/app/repositories/FilePSRepository.java @@ -0,0 +1,15 @@ +package app.repositories; + +import app.entities.db.DbFile; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.PagingAndSortingRepository; + +import java.util.UUID; + +public interface FilePSRepository extends PagingAndSortingRepository { + + @Query("select f from DbFile f where f.deleted = false") + Page getFiles(Pageable pageable); +} diff --git a/src/main/java/app/repositories/FileRepository.java b/src/main/java/app/repositories/FileRepository.java new file mode 100644 index 0000000..35db2f2 --- /dev/null +++ b/src/main/java/app/repositories/FileRepository.java @@ -0,0 +1,9 @@ +package app.repositories; + +import app.entities.db.DbFile; +import org.springframework.data.repository.CrudRepository; + +import java.util.UUID; + +public interface FileRepository extends CrudRepository { +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 128abd1..aa3c32d 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -18,6 +18,10 @@ spring: properties: hibernate: format_sql: ${SHOW_SQL:false} + servlet: + multipart: + max-file-size: 10MB + max-request-size: 10MB org: jobrunr: