Google TakeOut图片下载器
目录
介绍
Google为用户提供了一种通过Google TakeOut从Google照片下载数据的方法。用户可以下载包含所有(真的吗?)照片的zip文件。问题是Google选择性地将Json文件替换为zip中的图像文件。该json文件包含实际图像所在的链接。
本文介绍的实用程序将提取的takeput文件夹作为输入,并将所有实际图像下载到输出文件夹中。
背景
几天前,我妻子的Google帐户碰巧停止工作。经过调查,我发现她已经使用了所有15GB数据。罪魁祸首是图像文件。她的Android手机正在将手机上的每个图像同步到Google相册。
它给了我两个选择:
- 选择Google提供的其他付费存储空间。
- 下载图片并释放Google存储空间以解锁该帐户。
我选择了第二个选项。
我通过Google TakeOut下载了Google照片zip,给人的印象是,一旦下载zip,就可以完成。但是在提取zip时,我发现的是,Google压缩了json文件(其中包含指向实际图像文件的链接),而不是所有图像文件。
为了解决这个问题,我开发了一个实用程序,可以读取所有这些json文件并下载相应的图像。
使用应用程序
使用此应用程序之前,您需要:
- 从Google takeout下载并提取takeout zip。
在此处可以找到更多信息。
- 提取zip文件后,打开演示应用程序。
- 选择提取的目录作为输入目录。
- 选择一个输出目录作为您需要下载图像的位置。
- 点击“开始下载 ”。
现在坐下来放松。该应用程序会将所有图像下载到您的输出文件夹中。
关于源代码
在演示应用程序中仅添加了三个类:
- DownloadManager:执行实际的下载任务。
- IDownloadProgressNotifier:用于将下载进度信息返回给UI的界面。
- Form类:用于显示UI。它实现IDownloadProgressNotifier接口。
下载管理器
下载管理器执行以下工作:
- 从提取的文件夹中读取json文件以获取图像链接
- 从json文件中的链接下载图像文件
我们对json文件感兴趣的字段是:
- url:这是实际图像所在的链接。
- title:图像标题。我们将使用该名称将图像保存在本地。
json的片段如下所示:
{
"title": "IMG_20110711_192757772.jpg",
"description": "",
"url": "https://lh3.googleusercontent.com/-JHYN2OSYh21s/Vaxpt7adEp2I/
AAAAAAAABCu0/hIlcgO9TzwwkEJm2eQ9PcBu2rL1kPOqZWqwCLABGAYYCw/
s0-d/IMG_20110711_192757772.jpg",
"imageViews": "0",
使用下载管理器
要使用下载管理器,您需要通过向下载管理器的构造函数提供以下参数来创建下载管理器的对象。
- 输入目录
- 输出目录
- 告知是否继续出错的标记
- 下载进度通知程序处理程序
DownloadManager downloader = new DownloadManager(srcPath, dstPath, true, false, this);
现在调用StartDownload()。这将开始下载进度。下载进度将通知给通知程序处理程序,可用于更新UI。
downloader.StartDownload();
内部下载管理器
下载管理器只有一项public函数。
public void StartDownload()
StartDownload()函数
此函数将是所有处理的起点。它执行以下操作:
1、它调用该PreDownload函数来通知UI下载过程即将开始。
if (Notifier != null)
Notifier.PreDownload(TotalFilesCount);
2、调用CreateDirectory以创建输出根目录。
//
//Create Folder structure in the output directory.
//
CreateDirectory(new DirectoryInfo(Path.GetDirectoryName(inputRootFolder)));
3、调用TraverseFolderTree以遍历inputFolder 及其子文件夹中的所有文件。
TraverseFolderTree(inputRootFolder);
4、函数TraverseFolderTree完成下载所有文件后返回。完成后,通过调用notifier函数OnDownloadFinish()通知UI 。
if (Notifier != null)
Notifier.OnDownloadFinish();
TraverseFolderTree函数
此函数通过输入目录树结构进行。它以递归方式遍历输入目录结构,以检查所有扩展名为“ * .jpg.json ”的文件。我们仅对这些文件感兴趣。
首先,我们需要创建outputfolder路径。inputfolder和outputfolder的目录结构将保持不变。
示例:如果用户选择以下目录作为输入和输出目录。
- 输入目录:C:\Users\test\Desktop\TakeoutProject\Input
- 输出目录:C:\Users\test\Desktop\TakeoutProject\Output
如果当前输入处理文件夹为C:\Users\test\Desktop\TakeoutProject\Input\2015-07-17,则通过将输入根目录前缀替换为输出根目录来创建输出目录名称。
当前Output目录将是C:\Users\test\Desktop\TakeoutProject\Output\2015-07-17。
String sCurrentOutputFolder = sSeedFolder.Replace(inputRootFolder, outputRootFolder) ;
将创建输出目录,将在其中下载图像文件。
Directory.CreateDirectory(sCurrentOutputFolder);
现在在currentInputDirectory中枚举所有扩展名为“ * .jpg.json ” 的文件。这些文件是我们的候选文件。
//
// Now go through all files. Search for jpg link files only.
// "*.jpg.json" pattern will exclude other files like metadata.json
//
foreach (String strFile in Directory.EnumerateFiles
(sSeedFolder, JSON_JPEG_FILE_FILTER))
{
....
....
....
}
每个文件路径都传递给RetrieveImage 函数以读取文件并下载实际的图像文件。
RetrieveImage(strFile, sCurrentOutputFolder);
通过调用通告程序函数OnCurrentFileProgress通知UI 。如果发生错误,则通过调用notifier函数OnError通知UI 。
最后的循环如下所示:
//
// Now go through all files. Search for jpg link files only.
// "*.jpg.json" pattern will exclude other files like metadata.json
//
foreach (String strFile in Directory.EnumerateFiles
(sSeedFolder, JSON_JPEG_FILE_FILTER))
{
try
{
if (Notifier != null)
Notifier.OnCurrentFileProgress
(strFile, ProcessedFilesCount, TotalFilesCount);
RetrieveImage(strFile, sCurrentOutputFolder);
ProcessedFilesCount++;
}
catch (Exception exc)
{
if (Notifier != null)
Notifier.OnError(strFile, exc.Message);
if (stopOnError)
{
//
// We are done as we are supposed to stop on error.
//
throw exc;
}
}
}
现在,以递归方式遍历所有其余目录/子目录。
//
// Traverse through all directories recursively.
//
foreach (String strDirectory in Directory.EnumerateDirectories(sSeedFolder))
{
TraverseFolderTree(strDirectory);
}
RetrieveImage函数
此函数下载.jpeg.json文件,并读取它以获取实际图像文件位置。
private void RetrieveImage(String sJsonFilePath, String sImageFilePathOutputFolder)
{
JsonValue fullJson = JsonValue.Parse(System.IO.File.ReadAllText(sJsonFilePath));
String Title = fullJson["title"];
String url = fullJson["url"];
using (WebClient client = new WebClient())
{
client.DownloadFile(url, sImageFilePathOutputFolder +
Path.DirectorySeparatorChar + Title);
}
}
如前所述,我们仅对“title”和“url”字段感兴趣。
“url”代表文件的实际网址。WebClient类用于从此URL下载图像文件。输出文件名将与“title”的文件名相同。
using (WebClient client = new WebClient())
{
client.DownloadFile(url, sImageFilePathOutputFolder + Path.DirectorySeparatorChar + Title);
}
IdownloadProgressNotifier接口
这个interface声明了以下函数。下载管理器使用此接口来通知下载状态。
接口函数
OnCurrentFileProgress(String sCurrentFileName, int processedFilesCount, int totalFileCount)
该函数将当前文件和总进度通知处理程序。
PreDownload(int TotalFilesCount)
在开始下载之前,将调用此函数。
OnDownloadFinish();
下载完成后将调用此函数。
OnError(String sFileName, String sErrorInformation);
万一发生任何错误,将调用此函数。它传递发生错误的文件名以及错误信息。
Form1类
此类是拥有UI的简单C#表单类。结合图片中显示的简单UI元素,它实现了IDownloadProgressNotifier接口。对该类的引用将作为处理程序发送到下载管理器。IDownloadProgressNotifier接口函数的实现用于用进度状态更新UI。
IDownloadProgressNotifier接口实现
OnCurrentFileProgress(String sCurrentFileName,int processingFilesCount,int totalFileCount)
该函数将当前文件和总进度通知处理程序。此回调函数将progressbar递增1。这使得progressbar向前移动一个单位。
public void OnCurrentFileProgress
(string sCurrentFileName, int processedFilesCount, int totalFileCount)
{
pbTotalProgress.Increment(1);
}
PreDownload(int TotalFilesCount)
此回调函数用于初始化UI上的进度条。
public void PreDownload(int TotalFilesCount)
{
pbTotalProgress.Minimum = 0;
pbTotalProgress.Maximum = TotalFilesCount;
pbTotalProgress.Step = 1;
}
在开始下载之前,将调用此函数。
OnDownloadFinish();
下载完成后将调用此函数。此函数用于显示过程完成消息框。
public void OnDownloadFinish()
{
pbTotalProgress.Value = pbTotalProgress.Maximum;
MessageBox.Show("Process finished.", "Success");
}
OnError(String sFileName,String sErrorInformation);
此回调函数用于显示发生的错误。
public void OnError(string sFileName, string sErrorInformation)
{
MessageBox.Show("Process interrupted with an error
while processing file \n File Name :" +
sFileName +"\n ErrorInformation" + sErrorInformation, "Error" );
}
PS:个人觉得可以作为图片下载的参考,对于类似winform界面进度条的处理也是一个参考!
下一篇: css -- 水平和垂直居中的一些笔记