package ru.soune.nocopy.controller.file; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import ru.soune.nocopy.dto.file.FileEntityResponse; import ru.soune.nocopy.entity.AuthToken; import ru.soune.nocopy.exception.NotFoundAuthToken; import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.service.file.FileEntityService; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @Slf4j @RestController @RequestMapping("/api/files") @RequiredArgsConstructor public class FileDownloadController { private final FileEntityService fileEntityService; private final AuthTokenRepository authTokenRepository; /** * Скачать файл по ID FileEntity */ @GetMapping("/download/{fileId}") public ResponseEntity downloadFile( @PathVariable String fileId, @RequestHeader("Authorization") String tokenHeader) { try { Long userId = getUserIdFromToken(tokenHeader); FileEntityResponse fileInfo = fileEntityService.getById(fileId); if (!fileInfo.getUserId().equals(userId)) { return ResponseEntity.status(403).build(); } if (!fileInfo.isExistsOnDisk()) { return ResponseEntity.status(404) .body(null); } Path filePath = Paths.get(fileInfo.getFilePath()); Resource resource = new UrlResource(filePath.toUri()); if (!resource.exists()) { return ResponseEntity.status(404).build(); } String contentType = determineContentType(filePath); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(contentType)) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileInfo.getOriginalFileName() + "\"") .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileInfo.getFileSize())) .body(resource); } catch (NotFoundAuthToken e) { return ResponseEntity.status(401).build(); } catch (Exception e) { log.error("Error downloading file", e); return ResponseEntity.status(500).build(); } } /** * Получить предпросмотр файла (если поддерживается) */ @GetMapping("/preview/{fileId}") public ResponseEntity previewFile( @PathVariable String fileId, @RequestHeader("Authorization") String tokenHeader) { try { Long userId = getUserIdFromToken(tokenHeader); FileEntityResponse fileInfo = fileEntityService.getById(fileId); if (!fileInfo.getUserId().equals(userId)) { return ResponseEntity.status(403).build(); } if (!isPreviewSupported(fileInfo.getMimeType())) { return ResponseEntity.status(415) .body(null); } Path filePath = Paths.get(fileInfo.getFilePath()); Resource resource = new UrlResource(filePath.toUri()); if (!resource.exists()) { return ResponseEntity.status(404).build(); } String contentType = determineContentType(filePath); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(contentType)) .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + fileInfo.getOriginalFileName() + "\"") .body(resource); } catch (NotFoundAuthToken e) { return ResponseEntity.status(401).build(); } catch (Exception e) { log.error("Error previewing file", e); return ResponseEntity.status(500).build(); } } /** * Получить информацию о файле для фронтенда */ @GetMapping("/info/{fileId}") public ResponseEntity getFileInfo( @PathVariable String fileId, @RequestHeader("Authorization") String tokenHeader) { try { Long userId = getUserIdFromToken(tokenHeader); FileEntityResponse fileInfo = fileEntityService.getById(fileId); if (!fileInfo.getUserId().equals(userId)) { return ResponseEntity.status(403).build(); } return ResponseEntity.ok(fileInfo); } catch (NotFoundAuthToken e) { return ResponseEntity.status(401).build(); } catch (Exception e) { log.error("Error getting file info", e); return ResponseEntity.status(500).build(); } } private String determineContentType(Path filePath) throws IOException { String contentType = Files.probeContentType(filePath); if (contentType == null) { contentType = "application/octet-stream"; } return contentType; } private boolean isPreviewSupported(String mimeType) { if (mimeType == null) { return false; } return mimeType.startsWith("image/") || mimeType.startsWith("text/") || mimeType.equals("application/pdf") || mimeType.startsWith("video/") || mimeType.startsWith("audio/"); } private Long getUserIdFromToken(String tokenHeader) { String token = tokenHeader.replace("Bearer ", ""); AuthToken authToken = authTokenRepository.findByToken(token) .orElseThrow(() -> new NotFoundAuthToken("Token not found")); return authToken.getUser().getId(); } }