#6 Directory download as ZIP
This commit is contained in:
@@ -10,6 +10,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -22,6 +23,8 @@ import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@Service
|
||||
public class FileSystemService {
|
||||
@@ -148,7 +151,35 @@ public class FileSystemService {
|
||||
try {
|
||||
Files.copy(this.locationTracker.resolve(name), outputStream);
|
||||
} catch (IOException e) {
|
||||
throw new FileSystemServiceException("Could not get file", e);
|
||||
throw new FileSystemServiceException("Could not stream file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void stream(Path targetPath, ZipOutputStream outputStream) {
|
||||
try {
|
||||
Files.walkFileTree(targetPath, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if(Files.isDirectory(file)) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
final ZipEntry e = new ZipEntry(targetPath.relativize(file).toString());
|
||||
|
||||
e.setSize(getSize(file));
|
||||
e.setTime(System.currentTimeMillis());
|
||||
|
||||
outputStream.putNextEntry(e);
|
||||
|
||||
StreamUtils.copy(stream(file), outputStream);
|
||||
|
||||
outputStream.closeEntry();
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new FileSystemServiceException("Could not stream file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +187,15 @@ public class FileSystemService {
|
||||
try {
|
||||
return Files.newInputStream(this.locationTracker.resolve(name));
|
||||
} catch (IOException e) {
|
||||
throw new FileSystemServiceException("Could not get file", e);
|
||||
throw new FileSystemServiceException("Could not stream file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream stream(Path name) {
|
||||
try {
|
||||
return Files.newInputStream(name);
|
||||
} catch (IOException e) {
|
||||
throw new FileSystemServiceException("Could not stream file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +207,14 @@ public class FileSystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private long getSize(Path name) {
|
||||
try {
|
||||
return Files.size(name);
|
||||
} catch (IOException e) {
|
||||
throw new FileSystemServiceException("Could not get file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMimeType(String name) {
|
||||
try {
|
||||
final String detectedMimeType = this.mimeTypeDetector.detectMimeType(this.locationTracker.resolve(name));
|
||||
|
||||
@@ -17,8 +17,8 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
@@ -30,10 +30,10 @@ import org.springframework.web.util.UriUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.swing.text.html.Option;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
@@ -44,6 +44,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@Controller
|
||||
public class FilesController implements InitializingBean {
|
||||
@@ -147,22 +148,45 @@ public class FilesController implements InitializingBean {
|
||||
}
|
||||
|
||||
@GetMapping("/files/download")
|
||||
public ResponseEntity downloadFile(String filename) {
|
||||
// TODO download of directories via .zip? Also needs to be streaming
|
||||
public ResponseEntity downloadFile(HttpServletResponse response, String filename) {
|
||||
final Path targetPath = this.locationTracker.resolve(filename);
|
||||
|
||||
try {
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||
.header(HttpHeaders.CONTENT_TYPE, this.fileSystemService.getMimeType(filename))
|
||||
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(this.fileSystemService.getSize(filename)))
|
||||
.body(new InputStreamResource(this.fileSystemService.stream(filename)));
|
||||
} catch (RuntimeException e) {
|
||||
if(Files.isDirectory(targetPath)) {
|
||||
downloadDirectory(targetPath, filename, response);
|
||||
|
||||
return ResponseEntity.ok(":)");
|
||||
}
|
||||
else {
|
||||
return downloadSingleFile(filename);
|
||||
}
|
||||
} catch (RuntimeException | IOException e) {
|
||||
logger.error("Could not get file", e);
|
||||
|
||||
return ResponseEntity.internalServerError().body(":(");
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseEntity downloadSingleFile(String filename) {
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
|
||||
.header(HttpHeaders.CONTENT_TYPE, this.fileSystemService.getMimeType(filename))
|
||||
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(this.fileSystemService.getSize(filename)))
|
||||
.body(new InputStreamResource(this.fileSystemService.stream(filename)));
|
||||
}
|
||||
|
||||
private void downloadDirectory(Path targetPath, String filename, HttpServletResponse response) throws IOException {
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename + ".zip");
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
|
||||
try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {
|
||||
fileSystemService.stream(targetPath, zippedOut);
|
||||
|
||||
zippedOut.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/files/upload")
|
||||
public String uploadFiles(@RequestParam("files") MultipartFile[] files) {
|
||||
for (MultipartFile file : files) {
|
||||
@@ -256,7 +280,7 @@ public class FilesController implements InitializingBean {
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
|
||||
.header(HttpHeaders.CONTENT_TYPE, this.fileSystemService.getMimeType(filename))
|
||||
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(this.fileSystemService.getSize(filename)))
|
||||
.body(new InputStreamResource(new ObservableInputStream(this.fileSystemService.stream(filename), new ObservableInputStream.Observer() {
|
||||
@@ -279,7 +303,7 @@ public class FilesController implements InitializingBean {
|
||||
try {
|
||||
response.setContentType(this.fileSystemService.getMimeType(filename));
|
||||
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(this.fileSystemService.getSize(filename)));
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "filename=\"" + filename + "\"");
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "filename=" + filename);
|
||||
|
||||
this.fileSystemService.stream(filename, response.getOutputStream());
|
||||
} catch (FileSystemServiceException | IOException e) {
|
||||
|
||||
@@ -90,8 +90,7 @@
|
||||
<a th:href="@{/files/delete(filename=${fileEntry.name})}"
|
||||
th:text="#{nbscloud.files-content-table.table.actions.delete}"/>
|
||||
</div>
|
||||
<div th:if="${!fileEntry.directory}"
|
||||
id="files-content-table-show-actions-detail-container-download">
|
||||
<div id="files-content-table-show-actions-detail-container-download">
|
||||
<a th:href="@{/files/download(filename=${fileEntry.name})}"
|
||||
th:text="#{nbscloud.files-content-table.table.actions.download}"/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user