mirror of https://github.com/micromata/borgbackup-butler.git

Kai Reinhard
17.59.2018 a4fc24b20d685320e34c71913a20bcfc056ce484
Handling of recreating files and directories works now fine (opens a file dialog with the restored items).
4 files modified
159 ■■■■ changed files
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java 22 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java 33 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java 93 ●●●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/archives/FileListEntry.jsx 11 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/BorgCommands.java
@@ -9,6 +9,7 @@
import de.micromata.borgbutler.json.JsonUtils;
import de.micromata.borgbutler.json.borg.*;
import de.micromata.borgbutler.utils.DateUtils;
import de.micromata.borgbutler.utils.ReplaceUtils;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.exec.CommandLine;
@@ -20,6 +21,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.Context;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
@@ -157,12 +159,24 @@
        return content;
    }
    public static Path extractFiles(BorgRepoConfig repoConfig, String archive, String path) throws IOException {
        Path tempDirWithPrefix = Files.createTempDirectory("borgbutler-extract-");
        Context context = new Context().setWorkingDir(tempDirWithPrefix.toFile()).setRepoConfig(repoConfig)
    /**
     * Stores the file in a subdirectory named with the repos display name.
     * @param restoreHomeDir
     * @param repoConfig
     * @param archive
     * @param path
     * @return Used sub directory with the restored content.
     * @throws IOException
     */
    public static File extractFiles(File restoreHomeDir, BorgRepoConfig repoConfig, String archive, String path) throws IOException {
        File restoreDir = new File(restoreHomeDir, ReplaceUtils.encodeFilename(repoConfig.getDisplayName(), true));
        if (!restoreDir.exists()) {
            restoreDir.mkdirs();
        }
        Context context = new Context().setWorkingDir(restoreDir).setRepoConfig(repoConfig)
                .setCommand("extract").setArchive(archive).setArgs(path);
        execute(context);
        return tempDirWithPrefix;
        return restoreDir;
    }
    private static String execute(Context context) {
borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
@@ -1,13 +1,23 @@
package de.micromata.borgbutler.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.micromata.borgbutler.BorgCommands;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Configuration {
    private Logger log = LoggerFactory.getLogger(Configuration.class);
    /**
     * Default dir name for restoring archives.
     */
    private static final String RESTORE_DIRNAME = "restore";
    @Getter
    private String borgCommand = "borg";
    /**
@@ -17,6 +27,15 @@
    @JsonProperty("cache_archive_content_max_disc_size_mb")
    private int cacheArchiveContentMaxDiscSizeMB = 100;
    /**
     * Default is restore inside BorgButler's home dir (~/.borgbutler/restore).
     */
    @Getter
    @JsonProperty("restore_dir")
    private String restoreDirPath;
    @JsonIgnore
    private File restoreHomeDir;
    @Getter
    @JsonProperty("repo-configs")
    private List<BorgRepoConfig> repoConfigs = new ArrayList<>();
@@ -36,4 +55,18 @@
        }
        return null;
    }
    public File getRestoreHomeDir() {
        if (restoreHomeDir == null) {
            if (StringUtils.isNotBlank(restoreDirPath)) {
                restoreHomeDir = new File(restoreDirPath);
            } else {
                restoreHomeDir = new File(ConfigurationHandler.getInstance().getWorkingDir(), RESTORE_DIRNAME);
            }
            if (!restoreHomeDir.exists()) {
                log.info("Creating dir '" + restoreHomeDir.getAbsolutePath() + "' for restoring backup files and directories.");
            }
        }
        return restoreHomeDir;
    }
}
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
@@ -26,6 +26,7 @@
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
@Path("/archives")
@@ -79,14 +80,14 @@
    }
    @GET
    @Path("/download")
    @Path("/restore")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    /**
     * @param archiveId
     * @param fileNumber The fileNumber of the file in the archive served by BorgButler's
     * @param fileNumber The fileNumber of the file or directory 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) {
    public Response restore(@QueryParam("archiveId") String archiveId, @QueryParam("fileNumber") int fileNumber) {
        log.info("Requesting file #" + fileNumber + " of archive '" + archiveId + "'.");
        FileSystemFilter filter = new FileSystemFilter().setFileNumber(fileNumber);
        List<BorgFilesystemItem> items = ButlerCache.getInstance().getArchiveContent(archiveId, false,
@@ -94,7 +95,7 @@
        if (CollectionUtils.isEmpty(items)) {
            log.error("Requested file #" + fileNumber + " not found in archive '" + archiveId
                    + ". (May-be the archive content isn't yet loaded to the cache.");
            Response.ResponseBuilder builder = Response.status(404);
            Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
            return builder.build();
        }
        if (items.size() != 1) {
@@ -103,18 +104,63 @@
            Response.ResponseBuilder builder = Response.status(404);
            return builder.build();
        }
        BorgFilesystemItem item = items.get(0);
        Archive archive = ButlerCache.getInstance().getArchive(archiveId);
        if (archive == null) {
            Response.ResponseBuilder builder = Response.status(404);
            Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
            return builder.build();
        }
        BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(archive.getRepoId());
        java.nio.file.Path path = null;
        java.nio.file.Path tempDir = null;
        try {
            tempDir = BorgCommands.extractFiles(repoConfig, archive.getName(), item.getPath());
            openFileBrowser(tempDir);
            BorgFilesystemItem item = items.get(0);
            File restoreHomeDir = ConfigurationHandler.getConfiguration().getRestoreHomeDir();
            File restoreDir = BorgCommands.extractFiles(restoreHomeDir, repoConfig, archive.getName(), item.getPath());
            List<java.nio.file.Path> files = DirUtils.listFiles(restoreDir.toPath());
            if (CollectionUtils.isEmpty(files)) {
                log.error("No files extracted.");
                Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
                return builder.build();
            }
            openFileBrowser(new File(restoreDir, item.getPath()));
            Response.ResponseBuilder builder = Response.status(Response.Status.ACCEPTED);
            return builder.build();
        } catch (IOException ex) {
            log.error("No file extracted: " + ex.getMessage(), ex);
            Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
            return builder.build();
        }
    }
    public void openFileBrowser(File directory) {
        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE_FILE_DIR)) {
            Desktop.getDesktop().browseFileDirectory(directory);
        }
    }
    private Response handleRestoredFiles(BorgRepoConfig repoConfig, Archive archive) {
        // Todo: Handle download from single files as well as download of zip archive (if BorgButler runs remote).
        return null;
       /* File file = path.toFile();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            FileUtils.copyFile(file, baos);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
        }
        BorgFilesystemItem item = items.get(0);
        file = new File(item.getPath());
        byte[] byteArray = baos.toByteArray();//result.getAsByteArrayOutputStream().toByteArray();
        Response.ResponseBuilder builder = Response.ok(byteArray);
        builder.header("Content-Disposition", "attachment; filename=" + file.getName());
        // Needed to get the Content-Disposition by client:
        builder.header("Access-Control-Expose-Headers", "Content-Disposition");
        Response response = builder.build();
        return response;
        try {
            //java.nio.file.Path tempDirWithPrefix = Files.createTempDirectory("borgbutler-extract-");
            File restoreHomeDir = ConfigurationHandler.getConfiguration().getRestoreHomeDir();
            File restoreDir = BorgCommands.extractFiles(restoreHomeDir, repoConfig, archive.getName(), item.getPath());
            openFileBrowser(restoreDir);
            List<java.nio.file.Path> files = DirUtils.listFiles(tempDir);
            if (CollectionUtils.isEmpty(files)) {
                log.error("No file extracted.");
@@ -122,39 +168,18 @@
                return builder.build();
            }
            path = files.get(0);
            File file = path.toFile();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                FileUtils.copyFile(file, baos);
            } catch (IOException ex) {
                log.error(ex.getMessage(), ex);
            }
            file = new File(item.getPath());
            byte[] byteArray = baos.toByteArray();//result.getAsByteArrayOutputStream().toByteArray();
            Response.ResponseBuilder builder = Response.ok(byteArray);
            builder.header("Content-Disposition", "attachment; filename=" + file.getName());
            // Needed to get the Content-Disposition by client:
            builder.header("Access-Control-Expose-Headers", "Content-Disposition");
            Response response = builder.build();
            return response;
        } catch (IOException ex) {
            log.error("No file extracted: " + ex.getMessage(), ex);
            Response.ResponseBuilder builder = Response.status(404);
            return builder.build();
        } finally {
/*            if (tempDir != null) {
           if (tempDir != null) {
                try {
                    FileUtils.deleteDirectory(tempDir.toFile());
                } catch (IOException ex) {
                    log.error("Error while trying to delete temporary directory '" + tempDir.toString() + "': " + ex.getMessage(), ex);
                }
            }*/
        }
    }
    public static void openFileBrowser(java.nio.file.Path path) {
        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE_FILE_DIR)) {
            Desktop.getDesktop().browseFileDirectory(path.toFile());
        }
            }
        }*/
    }
}
borgbutler-webapp/src/components/views/archives/FileListEntry.jsx
@@ -7,20 +7,25 @@
function download(archiveId, fileNumber) {
    let filename;
    fetch(getRestServiceUrl('archives/download', {
    fetch(getRestServiceUrl('archives/restore', {
        archiveId: archiveId,
        fileNumber: fileNumber
    }))
        .then(response => {
            if (response.status === 202) { // ACCEPTED
                // No download wanted (file or path was only restored on server).
               return undefined;
            }
            if (!response.ok) {
                throw new Error(response.statusText);
            }
            filename = getResponseHeaderFilename(response.headers.get('Content-Disposition'));
            return response.blob();
        })
        .then(blob => {
            fileDownload(blob, filename)
            if (filename) {
                fileDownload(blob, filename);
            }
        })
        .catch(error => {
            console.log(error.toString());