[C#/WinForm] 無名關站備份相簿(下載實體照片)的懶人程式分享
自己寫的這支程式主要搭配2013.9.2開始,無名小站公告終止服務的相簿備份圖片下載
載點在文章最後面
前言
無名小站要關站了~
若照著官網說明,自己的無名資料備份完會出現一個超連結
從超連結下載完.zip壓縮檔,但是
解壓完不會有實體照片,只有一堆相簿編號的.txt檔,裡面只有超連結Orz…
回到官網FAQ查看
還需要另外找軟體,下載實體檔案圖片,而且官網介紹的軟體,麻煩的是還要一個一個.txt檔打開做匯入動作
那相簿如果有100多本,不就開.txt開100次匯到手軟O_O?
所以…自己來客製化好了…
程式目標只要讓使用者輸入相簿的detail.html檔所在檔案總管路徑
程式就自動下載檔案到detail.html相同的目錄下,並依相簿名稱做資料夾分類,減少使用者的操作,免安裝綠色版(不過由於使用.net 4技術開發,XP系統可能要先裝.net framework 4才能執行)
因為我只有備份相簿的需求,影片、MP3的檔案此程式不支援,所以開放Source Code,看有需要的人就自行改寫吧
邏輯說明都在註解裡,匆匆忙忙花一個早上寫完,架構有點亂,程式品質不保證XD
程式碼實作
UI設計
後置程式碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using HtmlAgilityPack;
using System.Net;
using System.Security.Policy;
using System.Threading;
namespace WretchAlbumBackupDownload
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
btnGo.Enabled = true;
btnStop.Enabled = false;
}
//抓圖Thread
public Thread mainThread = null;
/// <summary>
/// 開始下載
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnGo_Click(object sender, EventArgs e)
{
mainThread = new Thread(new ParameterizedThreadStart((o) =>
{
try
{
//detail.html檔路徑
string detailHtmlPath = txtDetailHtmlPath.Text.Trim();
if (File.Exists(detailHtmlPath))
{ //detail.html檔有存在的話
btnGo.Invoke(new Action(() => {
btnGo.Enabled = false;
}));
btnStop.Invoke(new Action(() => {
btnStop.Enabled = true;
}));
//detail.html的資料夾位置
string detailHtmlDir = Path.GetDirectoryName(detailHtmlPath);
//打開detail.html解析有哪些.txt檔
HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
using (FileStream fs = new FileStream(detailHtmlPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
//撈html資料進HtmlDocument物件
htmlDoc.Load(fs, Encoding.UTF8);
IEnumerable<HtmlNode> tableNodes = htmlDoc.DocumentNode.Descendants("table");
//儲存相簿名稱及其圖片的超連結
Dictionary<string, List<string>> albums = new Dictionary<string, List<string>>();
foreach (HtmlNode table in tableNodes)
{
//取得相簿名稱
string albumTitle = table.SelectSingleNode("tr[2]/td[1]").InnerText;
//圖片超連結儲存
List<string> picUrls = new List<string>();
//取得有.txt檔名
string txtFileName = table.SelectSingleNode("tr[6]/td[1]").InnerText;
//txt檔的路徑
string txtFilePath = Path.Combine(detailHtmlDir, txtFileName);
//檢查該.txt存在
if (File.Exists(txtFilePath))
{
//打開.txt檔
using (FileStream fsTxt = new FileStream(txtFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
using (StreamReader sr = new StreamReader(fsTxt))
{
//暫存圖片Url的變數
string picUrl = string.Empty;
//一列一列地讀取照片Url
while ((picUrl = sr.ReadLine()) != null)
{
if (picUrl.StartsWith("http"))
{
picUrls.Add(picUrl);
}
}
//相簿名稱防呆重覆
if (albums.Keys.Contains(albumTitle))
{
albumTitle = albumTitle +"_"+ Guid.NewGuid().ToString();//取不重覆的名稱
}
//加入一本相簿
albums.Add(albumTitle, picUrls);
}
}//End using
}//End if
}//End foreach
//有相簿資料待下載
if (albums.Count > 0)
{
//走訪每本相簿
foreach (KeyValuePair<string, List<string>> album in albums)
{
//將相簿名稱置換成檔案總管可接受的名稱
string albumTitle = this.ReplaceUrlToOK(album.Key);
//相簿預計存放的位置
string albumDirPath = Path.Combine(detailHtmlDir, albumTitle);
if (!Directory.Exists(albumDirPath))
{//建立相簿資料夾
Directory.CreateDirectory(albumDirPath);
}
//下載此相簿的圖片
foreach (string picHttpUrl in album.Value)
{
if (!string.IsNullOrEmpty(picHttpUrl))
{
this.DonwloadOnePic(picHttpUrl, albumDirPath);
txtLog.Invoke(new Action(() =>
{
txtLog.AppendText("下載完圖片:" + picHttpUrl + "。保存目錄:" + albumDirPath);
}));
}
}
}
}
//下載完成
txtLog.Invoke(new Action(()=>{
txtLog.AppendText(Environment.NewLine+"檔案已全部下載完成" + Environment.NewLine);
}));
btnGo.Invoke(new Action(() => {
btnGo.Enabled = true;
}));
btnStop.Invoke(new Action(() => {
btnStop.Enabled = false;
}));
}//End using
}//End if
}
catch (Exception ex)
{
txtLog.Invoke(new Action( ()=>{
txtLog.AppendText( ex.Message + Environment.NewLine);
} ));
this.AbortMainThread();
}
}));
//啟動執行緒
mainThread.Start();
}
/// <summary>
/// 中止下載
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStop_Click(object sender, EventArgs e)
{
try
{
//中止執行緒
this.AbortMainThread();
btnGo.Enabled = true;
btnStop.Enabled = false;
}
catch (Exception ex)
{
// txtLog.AppendText("程式發生錯誤:" + ex.Message + Environment.NewLine);
}
}
/// <summary>
/// 中止執行緒
/// </summary>
private void AbortMainThread()
{
if (mainThread != null)
{
try
{
}
finally
{
mainThread.Abort();
}
}
}
/// <summary>
/// 存放目錄用
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private string ReplaceUrlToOK(string url)
{
url = url.Replace("|", "").Replace("\"", "”");
url = url.Replace("`", "‘");//.Replace("-", "-");
//url = url.Replace("=", "=")
url = url.Replace("[", "〔");
url = url.Replace("]", "〕").Replace("\\", "\");
url = url.Replace(";", ";").Replace("'", "’");
url = url.Replace(",", ",");//.Replace(".", ".");
url = url.Replace("/", "/").Replace("~", "~");
url = url.Replace("!", "!").Replace("@", "@");
url = url.Replace("#", "#").Replace("$", "$");
url = url.Replace("%", "%").Replace("^", "^");
url = url.Replace("&", "&").Replace("*", "*");
// url = url.Replace("(", "(").Replace(")", ")");
url = url.Replace("_", "_").Replace("+", "+");
url = url.Replace(":", ":").Replace("\"", "”");
url = url.Replace("<", "<").Replace(">", ">");
url = url.Replace("?", "?");
return url;
}
/// <summary>
/// 下載一張圖片
/// </summary>
/// <param name="PicUrl">只支援Http開頭的Url或檔名,相對路徑不算</param>
/// <param name="SaveLocalPath"></param>
/// <returns></returns>
private void DonwloadOnePic( string picHttpUrl, string albumDirPath)
{
string picFileName =Path.GetFileName(picHttpUrl).Split('?')[0];
//預計存放圖片位置
string picFilePath = Path.Combine(albumDirPath, picFileName);
//解決檔名重覆
if (File.Exists(picFilePath))
{
string newFileName = Path.GetFileNameWithoutExtension(picFileName) + "(" + Guid.NewGuid().ToString() + ")" + Path.GetExtension(picFileName);
//新的存放圖檔路徑名稱
picFilePath = Path.Combine(albumDirPath, newFileName);
}
try
{
int maxRetryCount = 5;//下載重試次數最多5次
int retryCount = 1;
do
{
//下載
long[] totalSize = this.DownloadFile_WebRequest(picHttpUrl, picFilePath);
Thread.Sleep(100);
//驗證檔案是否下載完全
if (totalSize[0] != totalSize[1])
{//下載不完全
if (retryCount >= maxRetryCount)
{
txtLog.Invoke(new Action(() => {
txtLog.AppendText("✕下載檔案:" + picHttpUrl + " 已下載" + maxRetryCount + "次仍不完全,存放位置:" + picFilePath);
}));
break;//離開迴圈
}
else
{
retryCount++;
}
}
else
{//下載完全
break;//離開迴圈
}
} while (true);
}
catch (Exception ex)
{
txtLog.Invoke(new Action(() => {
txtLog.AppendText("下載一張圖片發生例外(" + picHttpUrl + "。保存位置:" + picFilePath + "):" + ex.Message);
}));
}
}
/// <summary>
/// 使用HttpWebRequest 下載檔案
/// </summary>
/// <param name="httpUrlHaveFileSpot"></param>
/// <param name="httpFileUrl"></param>
/// <param name="localFilePath"></param>
/// <returns></returns>
private long[] DownloadFile_WebRequest( string httpFileUrl,
string localFilePath)
{
//檔案總下載大小
long bytesProcessed = 0;
//Web上紀錄的檔案大小
long WebContentLength = 0;
Stream remoteStream = null;
Stream localStream = null;
HttpWebResponse response = null;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(httpFileUrl);
if (request != null)
{
request.Method = "GET";
request.Accept = "text/html, application/xhtml+xml, */*";
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
request.Headers.Add("Accept-Language:zh-TW");
request.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)";
request.KeepAlive = true;
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
//發出request
response = (HttpWebResponse)request.GetResponse();
if (response != null)
{
remoteStream = response.GetResponseStream();
//取得Web檔案大小
long.TryParse(response.Headers.Get("Content-Length"), out WebContentLength);
localStream = new FileStream(localFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead=remoteStream.Read(buffer, 0, buffer.Length))>0)
{
//一點一點地抓下圖片
localStream.Write(buffer, 0, bytesRead);
//累加下載檔案大小
bytesProcessed += bytesRead;
}
}
}
}
catch (Exception e)
{
txtLog.Invoke(new Action(() => {
txtLog.AppendText("下載一張圖片發生例外(httpFileUrl:" + httpFileUrl + "。保存位置:" + localFilePath + "):" + e.Message + Environment.NewLine);
}));
}
finally
{
if (response != null) response.Close();
if (remoteStream != null) remoteStream.Close();
if (localStream != null) localStream.Close();
}
return new long[] { bytesProcessed, WebContentLength };
}
}
}
程式使用方法&注意事項
1.首先從無名官網先進行資料備份,並下載解壓備份檔,再進到裡面的album資料夾,可以找到detail.html檔及一堆.txt檔
那堆.txt檔裡就是實體照片的超連結,detail.html檔和.txt檔要維持預設在同一個資料夾內
2.然後把detail.html檔的完整路徑輸入到程式裡,按下「開始下載」※執行WretchAlbumBackupDownload.exe這個程式
3. 等檔案下載全部完成後,再回到剛剛的album資料夾,即可看到實體檔案
結語
操作應該很簡單,程式有Bug請再留言通知囉~
開發人員下載 專案檔
一般使用者下載 執行檔
2013.9.19 程式更新:
相簿無照片超連結時,就不下載該相簿的圖片
2013.9.23 程式更新:
相簿名稱重覆的話,自動命名相簿名稱