Esempio n. 1
1
        /// <summary>
        /// Warning, if the book already exists in the location, this is going to delete it an over-write it. So it's up to the caller to check the sanity of that.
        /// </summary>
        /// <param name="storageKeyOfBookFolder"></param>
        public string DownloadBook(string bucketName, string storageKeyOfBookFolder, string pathToDestinationParentDirectory,
			IProgressDialog downloadProgress = null)
        {
            //review: should we instead save to a newly created folder so that we don't have to worry about the
            //other folder existing already? Todo: add a test for that first.

            // We need to download individual files to avoid downloading unwanted files (PDFs and thumbs.db to
            // be specific).  See https://silbloom.myjetbrains.com/youtrack/issue/BL-2312.  So we need the list
            // of items, not just the count.
            var matching = GetMatchingItems(bucketName, storageKeyOfBookFolder);
            var totalItems = CountDesiredFiles(matching);
            if(totalItems == 0)
                throw new DirectoryNotFoundException("The book we tried to download is no longer in the BloomLibrary");

            Debug.Assert(matching.S3Objects[0].Key.StartsWith(storageKeyOfBookFolder + "/"));

            // Get the top-level directory name of the book from the first object key.
            var bookFolderName = matching.S3Objects[0].Key.Substring(storageKeyOfBookFolder.Length + 1);
            while(bookFolderName.Contains("/"))
                bookFolderName = Path.GetDirectoryName(bookFolderName);

            // Amazon.S3 appears to truncate titles at 50 characters when building directory and filenames.  This means
            // that relative paths can be as long as 117 characters (2 * 50 + 2 for slashes + 15 for .BloomBookOrder).
            // So our temporary folder must be no more than 140 characters (allow some margin) since paths can be a
            // maximum of 260 characters in Windows.  (More margin than that may be needed because there's no guarantee
            // that image filenames are no longer than 65 characters.)  See https://jira.sil.org/browse/BL-1160.
            using(var tempDestination = new TemporaryFolder("BDS_" + Guid.NewGuid()))
            {
                var tempDirectory = Path.Combine(tempDestination.FolderPath, bookFolderName);
                if(downloadProgress != null)
                    downloadProgress.Invoke((Action) (() => { downloadProgress.ProgressRangeMaximum = totalItems; }));
                int booksDownloaded = 0;
                using(var transferUtility = new TransferUtility(_amazonS3))
                {
                    for(int i = 0; i < matching.S3Objects.Count; ++i)
                    {
                        var objKey = matching.S3Objects[i].Key;
                        if(AvoidThisFile(objKey))
                            continue;
                        // Removing the book's prefix from the object key, then using the remainder of the key
                        // in the filepath allows for nested subdirectories.
                        var filepath = objKey.Substring(storageKeyOfBookFolder.Length + 1);
                        // Download this file then bump progress.
                        var req = new TransferUtilityDownloadRequest()
                        {
                            BucketName = bucketName,
                            Key = objKey,
                            FilePath = Path.Combine(tempDestination.FolderPath, filepath)
                        };
                        transferUtility.Download(req);
                        ++booksDownloaded;
                        if(downloadProgress != null)
                            downloadProgress.Invoke((Action) (() => { downloadProgress.Progress = booksDownloaded; }));
                    }
                    var destinationPath = Path.Combine(pathToDestinationParentDirectory, bookFolderName);

                    //clear out anything existing on our target
                    var didDelete = false;
                    if(Directory.Exists(destinationPath))
                    {
                        try
                        {
                            SIL.IO.RobustIO.DeleteDirectory(destinationPath, true);
                            didDelete = true;
                        }
                        catch(IOException)
                        {
                            // can't delete it...see if we can copy into it.
                        }
                    }

                    //if we're on the same volume, we can just move it. Else copy it.
                    // It's important that books appear as nearly complete as possible, because a file watcher will very soon add the new
                    // book to the list of downloaded books the user can make new ones from, once it appears in the target directory.
                    bool done = false;
                    if(didDelete && PathUtilities.PathsAreOnSameVolume(pathToDestinationParentDirectory, tempDirectory))
                    {
                        try
                        {
                            SIL.IO.RobustIO.MoveDirectory(tempDirectory, destinationPath);
                            done = true;
                        }
                        catch(IOException)
                        {
                            // If moving didn't work we'll just try copying
                        }
                        catch(UnauthorizedAccessException)
                        {
                        }
                    }
                    if(!done)
                        done = CopyDirectory(tempDirectory, destinationPath);
                    if(!done)
                    {
                        var msg = LocalizationManager.GetString("Download.CopyFailed",
                            "Bloom downloaded the book but had problems making it available in Bloom. Please restart your computer and try again. If you get this message again, please click the 'Details' button and report the problem to the Bloom developers");
                        // The exception doesn't add much useful information but it triggers a version of the dialog with a Details button
                        // that leads to the yellow box and an easy way to send the report.
                        ErrorReport.NotifyUserOfProblem(new ApplicationException("File Copy problem"), msg);
                    }
                    return destinationPath;
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Warning, if the book already exists in the location, this is going to delete it an over-write it. So it's up to the caller to check the sanity of that.
        /// </summary>
        /// <param name="storageKeyOfBookFolder"></param>
        public string DownloadBook(string bucketName, string storageKeyOfBookFolder, string pathToDestinationParentDirectory,
                                   IProgressDialog downloadProgress = null)
        {
            //review: should we instead save to a newly created folder so that we don't have to worry about the
            //other folder existing already? Todo: add a test for that first.

            // We need to download individual files to avoid downloading unwanted files (PDFs and thumbs.db to
            // be specific).  See https://silbloom.myjetbrains.com/youtrack/issue/BL-2312.  So we need the list
            // of items, not just the count.
            var matching   = GetMatchingItems(bucketName, storageKeyOfBookFolder);
            var totalItems = CountDesiredFiles(matching);

            if (totalItems == 0)
            {
                throw new DirectoryNotFoundException("The book we tried to download is no longer in the BloomLibrary");
            }

            Debug.Assert(matching.S3Objects[0].Key.StartsWith(storageKeyOfBookFolder + "/"));

            // Get the top-level directory name of the book from the first object key.
            var bookFolderName = matching.S3Objects[0].Key.Substring(storageKeyOfBookFolder.Length + 1);

            while (bookFolderName.Contains("/"))
            {
                bookFolderName = Path.GetDirectoryName(bookFolderName);
            }

            // Amazon.S3 appears to truncate titles at 50 characters when building directory and filenames.  This means
            // that relative paths can be as long as 117 characters (2 * 50 + 2 for slashes + 15 for .BloomBookOrder).
            // So our temporary folder must be no more than 140 characters (allow some margin) since paths can be a
            // maximum of 260 characters in Windows.  (More margin than that may be needed because there's no guarantee
            // that image filenames are no longer than 65 characters.)  See https://jira.sil.org/browse/BL-1160.
            using (var tempDestination = new TemporaryFolder("BDS_" + Guid.NewGuid()))
            {
                var   tempDirectory = Path.Combine(tempDestination.FolderPath, bookFolderName);
                float progressStep  = 1.0F;
                float progress      = 0.0F;
                if (downloadProgress != null)
                {
                    downloadProgress.Invoke((Action)(() => {
                        // We cannot change ProgressRangeMaximum here because the worker thread is already active.
                        // So we calculate a step value instead.  See https://issues.bloomlibrary.org/youtrack/issue/BL-5443.
                        progressStep = (float)(downloadProgress.ProgressRangeMaximum - downloadProgress.Progress) / (float)totalItems;
                        progress = (float)downloadProgress.Progress;
                    }));
                }
                using (var transferUtility = new TransferUtility(_amazonS3))
                {
                    for (int i = 0; i < matching.S3Objects.Count; ++i)
                    {
                        var objKey = matching.S3Objects[i].Key;
                        if (AvoidThisFile(objKey))
                        {
                            continue;
                        }
                        // Removing the book's prefix from the object key, then using the remainder of the key
                        // in the filepath allows for nested subdirectories.
                        var filepath = objKey.Substring(storageKeyOfBookFolder.Length + 1);
                        // Download this file then bump progress.
                        var req = new TransferUtilityDownloadRequest()
                        {
                            BucketName = bucketName,
                            Key        = objKey,
                            FilePath   = Path.Combine(tempDestination.FolderPath, filepath)
                        };
                        transferUtility.Download(req);
                        if (downloadProgress != null)
                        {
                            downloadProgress.Invoke((Action)(() => {
                                progress += progressStep;
                                downloadProgress.Progress = (int)progress;
                            }));
                        }
                    }
                    var destinationPath = Path.Combine(pathToDestinationParentDirectory, bookFolderName);

                    //clear out anything existing on our target
                    var didDelete = false;
                    if (Directory.Exists(destinationPath))
                    {
                        try
                        {
                            SIL.IO.RobustIO.DeleteDirectory(destinationPath, true);
                            didDelete = true;
                        }
                        catch (IOException)
                        {
                            // can't delete it...see if we can copy into it.
                        }
                    }

                    //if we're on the same volume, we can just move it. Else copy it.
                    // It's important that books appear as nearly complete as possible, because a file watcher will very soon add the new
                    // book to the list of downloaded books the user can make new ones from, once it appears in the target directory.
                    bool done = false;
                    if (didDelete && PathUtilities.PathsAreOnSameVolume(pathToDestinationParentDirectory, tempDirectory))
                    {
                        try
                        {
                            SIL.IO.RobustIO.MoveDirectory(tempDirectory, destinationPath);
                            done = true;
                        }
                        catch (IOException)
                        {
                            // If moving didn't work we'll just try copying
                        }
                        catch (UnauthorizedAccessException)
                        {
                        }
                    }
                    if (!done)
                    {
                        done = CopyDirectory(tempDirectory, destinationPath);
                    }
                    if (!done)
                    {
                        var msg = LocalizationManager.GetString("Download.CopyFailed",
                                                                "Bloom downloaded the book but had problems making it available in Bloom. Please restart your computer and try again. If you get this message again, please click the 'Details' button and report the problem to the Bloom developers");
                        // The exception doesn't add much useful information but it triggers a version of the dialog with a Details button
                        // that leads to the yellow box and an easy way to send the report.
                        ErrorReport.NotifyUserOfProblem(new ApplicationException("File Copy problem"), msg);
                    }
                    return(destinationPath);
                }
            }
        }