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