From 8536692bec03913455a683e062288ea7c96c2f04 Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Sun, 16 Dec 2018 21:35:36 +0000
Subject: [PATCH] Recovery (download) of backup files started.
---
borgbutler-webapp/src/components/views/archives/FileListEntry.jsx | 35 ++++++++++++++++-
borgbutler-webapp/src/components/views/archives/FileListPanel.jsx | 1
borgbutler-webapp/src/components/views/archives/FileListTable.jsx | 6 ++-
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java | 21 ++++++++++
4 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
index 6cfe285..04e69dd 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
@@ -15,6 +15,7 @@
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import java.util.List;
@Path("/archives")
@@ -66,4 +67,24 @@
}
return JsonUtils.toJson(items, prettyPrinter);
}
+
+ @GET
+ @Path("/download")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ /**
+ * @param archiveId
+ * @param fileNumber The fileNumber of the file in the archive served by BorgButler's
+ * {@link #getArchiveFileLIst(String, String, String, boolean, boolean)}
+ */
+ public Response downloadFilebyPath(@QueryParam("archiveId") String archiveId, @QueryParam("fileNumber") int fileNumber) {
+ log.info("Downloading file #" + fileNumber + " of archive '" + archiveId + "'.");
+ return null;
+/* byte[] byteArray = result.getAsByteArrayOutputStream().toByteArray();
+ Response.ResponseBuilder builder = Response.ok(byteArray);
+ builder.header("Content-Disposition", "attachment; filename=" + filename);
+ // Needed to get the Content-Disposition by client:
+ builder.header("Access-Control-Expose-Headers", "Content-Disposition");
+ Response response = builder.build();
+ return response;*/
+ }
}
diff --git a/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx b/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
index d994b4b..90dfb83 100644
--- a/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
+++ b/borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
@@ -1,14 +1,45 @@
import React from 'react';
import PropTypes from 'prop-types';
import Highlight from 'react-highlighter';
-import {humanFileSize} from '../../../utilities/global';
+import {IconDownload} from '../../general/IconComponents';
+import {getResponseHeaderFilename, getRestServiceUrl, humanFileSize} from '../../../utilities/global';
+import fileDownload from 'js-file-download';
-function FileListEntry({entry, search}) {
+function download(archiveId, fileNumber) {
+ let filename;
+ fetch(getRestServiceUrl('archives/download', {
+ archiveId: archiveId,
+ fileNumber: fileNumber
+ }))
+ .then(response => {
+ if (!response.ok) {
+ throw new Error(response.statusText);
+ }
+
+ filename = getResponseHeaderFilename(response.headers.get('Content-Disposition'));
+ return response.blob();
+ })
+ .then(blob => {
+ this.setState({
+ running: false
+ });
+ fileDownload(blob, filename)
+ })
+ .catch(error => {
+ console.log(error.toString());
+ });
+}
+
+function FileListEntry({archiveId, entry, search}) {
return (
<tr>
<td className={'tt'}>{entry.mode}</td>
<td className={'tt'}>{entry.mtime}</td>
<td className={'tt'}>{humanFileSize(entry.size, true, true)}</td>
+ <td className={'tt'}>
+ <div className={'btn'} onClick={() => download(archiveId, entry.fileNumber)}>
+ <IconDownload/></div>
+ </td>
<td className={'tt'}><Highlight search={search}>{entry.path}</Highlight></td>
</tr>
);
diff --git a/borgbutler-webapp/src/components/views/archives/FileListPanel.jsx b/borgbutler-webapp/src/components/views/archives/FileListPanel.jsx
index f015c79..39c7701 100644
--- a/borgbutler-webapp/src/components/views/archives/FileListPanel.jsx
+++ b/borgbutler-webapp/src/components/views/archives/FileListPanel.jsx
@@ -87,6 +87,7 @@
}}
/>
<FileListTable
+ archiveId={this.props.archiveId}
entries={this.state.fileList}
search={this.state.filter.search}/>
</React.Fragment>;
diff --git a/borgbutler-webapp/src/components/views/archives/FileListTable.jsx b/borgbutler-webapp/src/components/views/archives/FileListTable.jsx
index a530328..a10e512 100644
--- a/borgbutler-webapp/src/components/views/archives/FileListTable.jsx
+++ b/borgbutler-webapp/src/components/views/archives/FileListTable.jsx
@@ -3,7 +3,7 @@
import {Table} from 'reactstrap';
import FileListEntry from './FileListEntry';
-function FileListTable({entries, search}) {
+function FileListTable({archiveId, entries, search}) {
const lowercaseSearch = search.split(' ')[0].toLowerCase();
return (
<Table striped bordered hover size={'sm'} responsive>
@@ -12,13 +12,14 @@
<th>Mode</th>
<th>Modified time</th>
<th>Size</th>
- <th>Path</th>
<th></th>
+ <th>Path</th>
</tr>
</thead>
<tbody>
{entries
.map((entry, index) => <FileListEntry
+ archiveId={archiveId}
entry={entry}
search={lowercaseSearch}
key={index}
@@ -29,6 +30,7 @@
}
FileListTable.propTypes = {
+ archiveId: PropTypes.string,
entries: PropTypes.array,
search: PropTypes.string
};
--
Gitblit v1.10.0