-
-
Notifications
You must be signed in to change notification settings - Fork 557
perf(api): Enable Http Caching & stream Book Content instead of load to memory #2494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
33a98c4
fe6a831
ef517a8
dd12b1c
95e94f8
db4c404
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,10 +22,8 @@ | |
| import jakarta.servlet.http.HttpServletResponse; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.core.io.ByteArrayResource; | ||
| import org.springframework.core.io.ClassPathResource; | ||
| import org.springframework.core.io.Resource; | ||
| import org.springframework.core.io.UrlResource; | ||
| import org.springframework.core.io.*; | ||
| import org.springframework.http.CacheControl; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.http.ResponseEntity; | ||
|
|
@@ -39,6 +37,7 @@ | |
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
| import java.time.Instant; | ||
| import java.util.*; | ||
| import java.util.stream.Collectors; | ||
|
|
||
|
|
@@ -265,13 +264,14 @@ public void downloadAllBookFiles(Long bookId, HttpServletResponse response) { | |
| bookDownloadService.downloadAllBookFiles(bookId, response); | ||
| } | ||
|
|
||
| public ResponseEntity<ByteArrayResource> getBookContent(long bookId) throws IOException { | ||
| public ResponseEntity<Resource> getBookContent(long bookId) throws IOException { | ||
| return getBookContent(bookId, null); | ||
| } | ||
|
|
||
| public ResponseEntity<ByteArrayResource> getBookContent(long bookId, String bookType) throws IOException { | ||
| public ResponseEntity<Resource> getBookContent(long bookId, String bookType) throws IOException { | ||
| BookEntity bookEntity = bookRepository.findById(bookId).orElseThrow(() -> ApiError.BOOK_NOT_FOUND.createException(bookId)); | ||
| String filePath; | ||
| Instant lastModified = null; | ||
| if (bookType != null) { | ||
| BookFileType requestedType = BookFileType.valueOf(bookType.toUpperCase()); | ||
| BookFileEntity bookFile = bookEntity.getBookFiles().stream() | ||
|
|
@@ -282,11 +282,18 @@ public ResponseEntity<ByteArrayResource> getBookContent(long bookId, String book | |
| } else { | ||
| filePath = FileUtils.getBookFullPath(bookEntity); | ||
| } | ||
| try (FileInputStream inputStream = new FileInputStream(filePath)) { | ||
| return ResponseEntity.ok() | ||
| .contentType(MediaType.APPLICATION_OCTET_STREAM) | ||
| .body(new ByteArrayResource(inputStream.readAllBytes())); | ||
|
|
||
| try { | ||
| lastModified = Files.getLastModifiedTime(Paths.get(filePath)).toInstant(); | ||
| } catch (IOException e) { | ||
| log.warn("Failed to get last modified time for: {}", filePath, e); | ||
| } | ||
|
|
||
| return ResponseEntity.ok() | ||
| .cacheControl(CacheControl.noCache().cachePrivate()) | ||
| .lastModified(lastModified != null ? lastModified : Instant.now()) // Fallback to now | ||
| .contentType(MediaType.APPLICATION_OCTET_STREAM) | ||
| .body(new InputStreamResource(new FileInputStream(filePath))); | ||
|
||
| } | ||
|
|
||
| @Transactional | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,10 +21,7 @@ | |
| import org.mockito.Mock; | ||
| import org.mockito.MockedStatic; | ||
| import org.mockito.junit.jupiter.MockitoExtension; | ||
| import org.springframework.core.io.ByteArrayResource; | ||
| import org.springframework.core.io.ClassPathResource; | ||
| import org.springframework.core.io.Resource; | ||
| import org.springframework.core.io.UrlResource; | ||
| import org.springframework.core.io.*; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
|
|
||
|
|
@@ -318,17 +315,18 @@ void downloadBook_delegatesToDownloadService() { | |
| } | ||
|
|
||
| @Test | ||
| void getBookContent_returnsByteArrayResource() throws Exception { | ||
| void getBookContent_returnsResource() throws Exception { | ||
| BookEntity entity = new BookEntity(); | ||
| entity.setId(10L); | ||
| when(bookRepository.findById(10L)).thenReturn(Optional.of(entity)); | ||
| Path path = Paths.get("/tmp/bookcontent.txt"); | ||
| Files.write(path, "hello".getBytes()); | ||
| try (MockedStatic<FileUtils> fileUtilsMock = mockStatic(FileUtils.class)) { | ||
| fileUtilsMock.when(() -> FileUtils.getBookFullPath(entity)).thenReturn(path.toString()); | ||
| ResponseEntity<ByteArrayResource> response = bookService.getBookContent(10L); | ||
| ResponseEntity<Resource> response = bookService.getBookContent(10L); | ||
| assertEquals(HttpStatus.OK, response.getStatusCode()); | ||
| assertArrayEquals("hello".getBytes(), response.getBody().getByteArray()); | ||
| assertNotNull(response.getBody()); | ||
| assertArrayEquals("hello".getBytes(), response.getBody().getContentAsByteArray()); | ||
|
Comment on lines
+328
to
+329
|
||
| } finally { | ||
| Files.deleteIfExists(path); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The codebase has an established convention of using FileSystemResource for serving files from disk, as seen in BookDownloadService.java:76-77, BookDropService.java:102, and CustomFontService.java:179. FileSystemResource provides the same streaming benefits as InputStreamResource but with automatic resource management and cleanup.
Consider using FileSystemResource instead of InputStreamResource to maintain consistency with the codebase and avoid resource leaks.