Add Download ability

master
Dmitry Isaenko 2024-01-10 21:59:11 +03:00
parent ae367797bb
commit 1791fd909d
5 changed files with 110 additions and 17 deletions

View File

@ -6,12 +6,9 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.extern.slf4j.Slf4j;
import ru.redrise.marinesco.data.InpEntryRepository;
import ru.redrise.marinesco.library.Author;
import ru.redrise.marinesco.library.InpEntry;
@Slf4j
@Controller
@RequestMapping("/book")
public class BookController {

View File

@ -0,0 +1,89 @@
package ru.redrise.marinesco;
import java.io.File;
import java.io.FileOutputStream;
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];
}
}
}
}

View File

@ -17,27 +17,29 @@ import ru.redrise.marinesco.data.AuthorRepository;
import ru.redrise.marinesco.data.GenreRepository;
import ru.redrise.marinesco.data.InpEntryRepository;
import ru.redrise.marinesco.data.LibraryMetadataRepository;
import ru.redrise.marinesco.settings.ApplicationSettings;
@Slf4j
@Component
@ConfigurationProperties(prefix = "marinesco.library")
public class InpxScanner implements Runnable {
private static volatile Thread parser;
private static volatile String lastRunErrors;
private String filesLocation = "";
private LibraryMetadata libraryMetadata;
private LibraryMetadataRepository libraryMetadataRepository;
private AuthorRepository authorRepository;
private GenreRepository genreRepository;
private InpEntryRepository inpEntryRepository;
public InpxScanner(AuthorRepository authorRepository,
private String filesLocation;
public InpxScanner(ApplicationSettings applicationSettings,
AuthorRepository authorRepository,
GenreRepository genreRepository,
InpEntryRepository inpEntryRepository,
LibraryMetadataRepository libraryMetadataRepository) {
this.filesLocation = applicationSettings.getFilesLocation();
this.authorRepository = authorRepository;
this.genreRepository = genreRepository;
this.inpEntryRepository = inpEntryRepository;
@ -47,7 +49,7 @@ public class InpxScanner implements Runnable {
/*
* @return true if executed, false if already running
*/
public boolean reScan(){
public boolean reScan() {
if (parser == null || !parser.isAlive()) {
parser = new Thread(this);
parser.start();
@ -190,14 +192,6 @@ public class InpxScanner implements Runnable {
return i + 1 < content.length && (content[i + 1] == '\r');
}
public String getFilesLocation() {
return filesLocation;
}
public void setFilesLocation(String location) {
filesLocation = location;
}
public static String getLastRunErrors() {
return lastRunErrors;
}

View File

@ -1,10 +1,14 @@
package ru.redrise.marinesco.settings;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "marinesco.library")
public class ApplicationSettings {
private static final String ALLOW_REGISTRATION = "allow_registration";
private String filesLocation = "";
private KeyValueRepository keyValueRepository;
@ -34,4 +38,13 @@ public class ApplicationSettings {
public synchronized boolean isRegistrationAllowed() {
return registrationAllowed;
}
public String getFilesLocation() {
return filesLocation;
}
public void setFilesLocation(String location) {
filesLocation = location;
}
}

View File

@ -29,7 +29,7 @@
<br /><span th:if="${book.addedDate} != null" th:text="${'Added : ' + book.addedDate}"></span>
<br /><span th:text="${'Size: ' + book.fileSize + ' bytes'}"></span>
<p>
<a th:href="'/'" th:text="Download"></a>
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName}" th:text="Download"></a>
</p>
</div>
</div>