Correct download process
This commit is contained in:
parent
dd7582b2d3
commit
57de3bea2f
10 changed files with 71 additions and 118 deletions
|
@ -1,88 +0,0 @@
|
|||
package ru.redrise.marinesco;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.redrise.marinesco.settings.ApplicationSettings;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/download")
|
||||
public class DownloadController {
|
||||
|
||||
private String filesLocation;
|
||||
|
||||
public DownloadController(ApplicationSettings applicationSettings) {
|
||||
this.filesLocation = applicationSettings.getFilesLocation();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/")
|
||||
public void getMethodName(@RequestParam String container,
|
||||
@RequestParam String file,
|
||||
HttpServletResponse response) throws Exception {
|
||||
|
||||
final FileSystemResource libraryLocation = new FileSystemResource(filesLocation + File.separator + container);
|
||||
try (ZipInputStream zipInputStream = new ZipInputStream(libraryLocation.getInputStream())) {
|
||||
ZipEntry zipEntry = zipInputStream.getNextEntry();
|
||||
|
||||
while (zipEntry != null) {
|
||||
if (zipEntry.getName().contains(file)) {
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
|
||||
.filename(file+".fb2", StandardCharsets.UTF_8) //TODO: fix
|
||||
.build()
|
||||
.toString());
|
||||
ServletOutputStream outStream = response.getOutputStream();
|
||||
sendFile(zipEntry.getSize(), zipInputStream, outStream);
|
||||
outStream.flush();
|
||||
outStream.close();
|
||||
return;
|
||||
}
|
||||
|
||||
zipEntry = zipInputStream.getNextEntry();
|
||||
}
|
||||
}
|
||||
throw new Exception("file not found " +
|
||||
filesLocation + File.separator + container + " → " + file);
|
||||
}
|
||||
|
||||
private void sendFile(long fileSize,
|
||||
ZipInputStream zipInputStream,
|
||||
ServletOutputStream outStream) throws Exception {
|
||||
|
||||
int blockSize = 0x200;
|
||||
|
||||
if (fileSize < 0x200)
|
||||
blockSize = (int) fileSize;
|
||||
|
||||
byte[] block = new byte[blockSize];
|
||||
long i = 0;
|
||||
|
||||
while (true) {
|
||||
int actuallyRead = zipInputStream.read(block);
|
||||
outStream.write(block, 0, actuallyRead);
|
||||
i += actuallyRead;
|
||||
if ((i + blockSize) > fileSize) {
|
||||
blockSize = (int) (fileSize - i);
|
||||
if (blockSize == 0)
|
||||
break;
|
||||
block = new byte[blockSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|||
@Configuration
|
||||
public class ThreadPoolTaskExecutorSettings {
|
||||
@Bean
|
||||
public TaskExecutor configTaskExecutor(){
|
||||
public ThreadPoolTaskExecutor configTaskExecutor(){
|
||||
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(8);
|
||||
executor.setMaxPoolSize(16);
|
||||
|
|
|
@ -8,16 +8,13 @@ import java.util.Set;
|
|||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.Transient;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.redrise.marinesco.RainbowDump;
|
||||
|
||||
@Slf4j
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
|
||||
|
@ -59,7 +56,7 @@ public class Book {
|
|||
this.libraryId = libraryId;
|
||||
this.libraryVersion = libraryVersion;
|
||||
this.id = new String(line).hashCode();
|
||||
this.container = container + ".zip";
|
||||
this.container = container;
|
||||
this.authors = new ArrayList<>();
|
||||
this.genres = new ArrayList<>();
|
||||
parseAuthors(authorsCollection);
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package ru.redrise.marinesco.library;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import ru.redrise.marinesco.settings.ApplicationSettings;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/download")
|
||||
public class DownloadController {
|
||||
|
||||
private final String filesLocation;
|
||||
|
||||
public DownloadController(ApplicationSettings applicationSettings) {
|
||||
this.filesLocation = applicationSettings.getFilesLocation();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/")
|
||||
public void getMethodName(@RequestParam String container,
|
||||
@RequestParam String file,
|
||||
HttpServletResponse response) throws Exception {
|
||||
try {
|
||||
File bookFile = new File(filesLocation + File.separator + container + File.separator + file);
|
||||
if (! bookFile.exists())
|
||||
throw new Exception("No file found :[");
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
ContentDisposition.attachment()
|
||||
.filename(file + ".fb2", StandardCharsets.UTF_8) // TODO: fix
|
||||
.build()
|
||||
.toString());
|
||||
|
||||
try (ServletOutputStream outStream = response.getOutputStream();
|
||||
FileInputStream inputStream = new FileInputStream(bookFile)) {
|
||||
IOUtils.copy(inputStream, outStream);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found [" + e.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,6 @@ package ru.redrise.marinesco.library;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -17,7 +14,7 @@ import java.util.zip.ZipEntry;
|
|||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -31,9 +28,8 @@ import ru.redrise.marinesco.settings.ApplicationSettings;
|
|||
@Component
|
||||
public class InpxScanner {
|
||||
private static volatile String lastRunErrors = "";
|
||||
private static LocalDateTime lastRunTime = LocalDateTime.of(1970, 01, 01, 0, 0, 0);
|
||||
|
||||
private TaskExecutor executor;
|
||||
private ThreadPoolTaskExecutor executor;
|
||||
private LibraryMetadataRepository libraryMetadataRepository;
|
||||
private AuthorRepository authorRepository;
|
||||
private GenreRepository genreRepository;
|
||||
|
@ -41,7 +37,7 @@ public class InpxScanner {
|
|||
|
||||
private String filesLocation;
|
||||
|
||||
public InpxScanner(TaskExecutor executor,
|
||||
public InpxScanner(ThreadPoolTaskExecutor executor,
|
||||
ApplicationSettings applicationSettings,
|
||||
AuthorRepository authorRepository,
|
||||
GenreRepository genreRepository,
|
||||
|
@ -59,15 +55,9 @@ public class InpxScanner {
|
|||
* @return true if executed, false otherwise
|
||||
*/
|
||||
public boolean reScan() {
|
||||
|
||||
LocalDateTime currentDateTime = LocalDateTime.now();
|
||||
|
||||
if (ChronoUnit.MINUTES.between(lastRunTime, currentDateTime) < 5) {
|
||||
lastRunErrors = "Too frequent requests. Please whait 5 min. Last attmpt: "
|
||||
+ lastRunTime.format(DateTimeFormatter.ofPattern("DD.MM.YYYY HH:mm:ss"));
|
||||
if (executor.getActiveCount() > 0)
|
||||
return false;
|
||||
}
|
||||
lastRunTime = currentDateTime;
|
||||
|
||||
lastRunErrors = "";
|
||||
|
||||
Thread scanThread = new Thread(() -> {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package ru.redrise.marinesco.library.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.comparator.Comparators;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
|
|
@ -5,8 +5,8 @@ spring:
|
|||
driver-class-name: org.h2.Driver
|
||||
generate-unique-name: false
|
||||
name: marinesco
|
||||
url: jdbc:h2:mem:marinesco
|
||||
# url: jdbc:h2:file:/tmp/h2
|
||||
# url: jdbc:h2:mem:marinesco
|
||||
# url: jdbc:h2:file:/tmp/h22
|
||||
username: sa
|
||||
password:
|
||||
jpa:
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
<div class="container base">
|
||||
<span class="validationError" th:if="${Error} != null" th:text="${Error}"></span>
|
||||
<div th:if="${author} != null">
|
||||
<div class="title" th:text="${author.authorName}"><br /></div>
|
||||
<div class="title" th:text="${author.authorName}"></div>
|
||||
<br />
|
||||
<div th:each="book : ${books}">
|
||||
<div class="wrapper">
|
||||
<div class="horizontal_el_left">
|
||||
|
@ -28,7 +29,7 @@
|
|||
</em>
|
||||
</div>
|
||||
<div class="horizontal_el_right">
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName}"
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName + '.' + book.fileExtension + '.zip'}"
|
||||
th:text="${'Download ' + book.fileExtension + ' (' + book.fileSizeForHumans + ')'}"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<br />
|
||||
<span th:each="genre : ${book.genres}" th:text="${((genre.humanReadableDescription == null || genre.humanReadableDescription == '') ? genre.genreId : genre.humanReadableDescription) + ' '}"></span>
|
||||
<p>
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName}"
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName + '.' + book.fileExtension + '.zip'}"
|
||||
th:text="${'Download ' + book.fileExtension + ' (' + book.fileSizeForHumans + ')'}"></a>
|
||||
</p>
|
||||
<div class="not_important" th:if="${book.serNo} != ''" th:text="${'Series # : ' + book.serNo}"></div>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<a th:href="${'/author/' + author.id}" th:text="${author.authorName}"></a>,
|
||||
</span>
|
||||
<br />
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName}"
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName + '.' + book.fileExtension + '.zip'}"
|
||||
th:text="Download">
|
||||
</a>
|
||||
<span th:text="${' (' + book.fileExtension + ' ' + book.fileSizeForHumans + ')'}"></span>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<a th:href="${'/author/' + author.id}" th:text="${author.authorName}"></a>,
|
||||
</span>
|
||||
<br />
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName}"
|
||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName + '.' + book.fileExtension + '.zip'}"
|
||||
th:text="Download">
|
||||
</a>
|
||||
<span th:text="${' (' + book.fileExtension + ' ' + book.fileSizeForHumans + ')'}"></span>
|
||||
|
|
Loading…
Reference in a new issue