这个问题已经困扰很久了 一直以来都没有解决 也不知道正规公司的数据库都是怎么备份的,上一次公司一台服务器出过一个问题所以提出了在公司服务器上边统一进行数据备份的方案,下边把我的步骤和大家分享一下希望高手指教。
一开始我使用的是局域网之内的磁盘映射实现的,因为当时从网上看到优快云有几个高手推荐使用这种方案然后我就使用了这中方法但是效果并不好因为我的备份工作不是在本机要把数据备份到局域网之内的其他机器上边 因为从上次的教训我知道在本机做备份遇到故障基本上没用,一开始不是很稳定,而且有时候磁盘映射直接失败。关键的是我在备份完成之后要先远传而且计算机的执行速度是很快的,所以这个其实不是一个很好的方法,真正解决了各种问题开始使用之后我又发现了另一个问题 因为我这边有十个供电公司所以网速参差不齐所以有些文件虽然传回来了但是实际上没有完成的传完 在还原的时候提示不完整,所以我尝试用Rar进行压缩然后进行远传,效果还好了 但也不是特别好。 后来终于使用了FTP 下边把源代码和大家分享
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Xml;
using Microsoft.Win32;
using System.IO;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Net;
namespace Remote_Backup_VS2005
{
[StructLayout(LayoutKind.Sequential)]
public struct NETRESOURCEA
{
public int dwScope;
public int dwType;
public int dwDisplayType;
public int dwUsage;
[MarshalAs(UnmanagedType.LPStr)]
public string lpLocalName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpRemoteName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpComment;
[MarshalAs(UnmanagedType.LPStr)]
public string lpProvider;
public override String ToString()
{
String str = "LocalName: " + lpLocalName + " RemoteName: " + lpRemoteName
+ " Comment: " + lpComment + " lpProvider: " + lpProvider;
return (str);
}
}
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
[System.Runtime.InteropServices.DllImport("mpr.dll ")]
public static extern int WNetAddConnection2A(
[MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource,
[MarshalAs(UnmanagedType.LPStr)] string lpPassword,
[MarshalAs(UnmanagedType.LPStr)] string UserName,
int dwFlags);
public static void Remote_Backup()
{
WriteLog("程序开始执行");
//使用FTP所以没有必要查看服务状态
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "server=.;database=cpudata;user id =sa;password=sa";
SqlCommand cmd = new SqlCommand();
cmd.CommandText = @"backup database cpudata to disk='c:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".bak' "; //更改为C盘
//cmd.CommandText = @"backup database cpudata to disk='c:/cpudata.bak'";
//backup database cpudata to disk ='c:/cpudata.bak'
cmd.Connection = conn;
//原来的时候需要记录文件个数对文件个数进行限制 现在不用了 所以程序注释掉
//SqlConnection conn_File_Limit = new SqlConnection();
//conn_File_Limit.ConnectionString = "server=" + GetValue("IP") + "," + GetValue("Port") + ";database=Check_Card_Database;user id =sa;password=19790601";
//SqlCommand command_Limite = new SqlCommand();
//command_Limite.Connection = conn_File_Limit;
//command_Limite.CommandText = "select count(ID) from Record where substring(Mark,1,4)='" + GetValue("ServerName") + "'";
//2011年5月9日19:37:00 修改程序全都压缩然后上传到服务器
try
{
conn.Open();
//conn_File_Limit.Open();
int i = cmd.ExecuteNonQuery(); //实现远程备份
if (i==-1)
{
WriteLog("备份数据库成功");
}
else
{
WriteLog("备份数据库失败");
}
//开始远程备份数据库流程 首先删除映射的磁盘
//Execute("net use * /delete /yes", 0);
string Company_Name = GetValue("ServerName").Trim().Substring(0,2);
//string NetUseCommand = @"net use z: //10.68.69.203/e$/各个供电公司数据库备份/东区数据库备份 yfyf /user:administrator";
//int result = DiskMap(Company_Name);
//if (result==0)
//{
// WriteLog("建立映射成功");
//}
//else if (result==85)
//{
// WriteLog("映射失败, 驱动器已经存在");
//}
//else if (result ==1202 || result ==1203)
//{
// WriteLog("映射失败,找不到网络路径");
//}
//else if (result == 1326)
//{
// WriteLog("映射失败,登录账号有误");
//}
//else
//{
// WriteLog("映射失败,可能有其他错误");
//}
//确认文件数量之后开始对文件进行压缩
CreatRAR("C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".bak", "C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar");
WriteLog("文件压缩成功");
Execute(@"del C:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".bak //yes", 0);
WriteLog("备份文件删除成功");
FtpUpDown ftpUpDown = new FtpUpDown("10.68.69.203", "administrator", "yfyf");
ftpUpDown.Upload("C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar", GetValue("ServerName").Trim().Substring(0, 2));
//string cmd_Copy_remote = @"copy C:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + @".rar z:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar";
// Execute(@"copy c:/cpudata.bak z:/cpudata.bak", 0);
//Execute(cmd_Copy_remote, 0);
WriteLog("文件传输成功");
//System.Threading.Thread.Sleep(5000);
Execute("del C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar", 0);
WriteLog("压缩文件删除成功");
//文件备份完成之后要删除多出指定文件数量的文件
string File_Count = GetValue("File_Count"); //已经备份的文件数量
string File_Limit = GetValue("File_Limit"); //备份文件数量限制
// string path = @"Z:/";
//FileInfo[] files = new DirectoryInfo(path).GetFiles();
//List<FileInfo> list = new List<FileInfo>(files);
//list.Sort(new Comparison<FileInfo>(delegate(FileInfo a, FileInfo b)
//{
// return a.CreationTime.CompareTo(b.CreationTime);
//}));
//WriteLog("现在的文件数量是"+list.Count.ToString().Trim());
////首先判断文件数量和文件限制数量之间比较
//if (int.Parse(list.Count.ToString())<int.Parse(File_Limit))
//{
//}
//else
//{
// //等于就要删除最早的文件
// list[0].Delete();
// WriteLog("文件删除成功");
//}
//UnRAR("Z://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar", "Z://" + DateTime.Now.ToString("yyyy-MM-dd-HH"));
//System.Threading.Thread.Sleep(180000);
//Execute("del z://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar",0);
//Execute("net use Z: /delete /yes", 0);
//WriteLog("映射删除成功");
//操作记录
//RecordLog();
//MessageBox.Show("Ok","提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
//conn_File_Limit.Close();
}
}
//函数用于启动磁盘映射
public static int DiskMap(string Company)
{
NETRESOURCEA[] n = new NETRESOURCEA[1];
n[0] = new NETRESOURCEA();
n[0].dwType = 1;
int dwFlags = 1;
n[0].lpLocalName = @"Z:";
n[0].lpRemoteName = @"//10.68.69.203/e$/各个供电公司数据库备份/" + Company + "数据库备份";
n[0].lpProvider = null;
Console.WriteLine(n[0]);
int res = WNetAddConnection2A(n, "yfyf", "administrator", dwFlags);
return res;
}
/// <summary>
/// 执行DOS命令,返回DOS命令的输出
/// </summary>
/// <param name="dosCommand">dos命令</param>
/// <param name="milliseconds">等待命令执行的时间(单位:毫秒),如果设定为0,则无限等待</param>
/// <returns>返回输出,如果发生异常,返回空字符串</returns>
public static string Execute(string dosCommand, int milliseconds)
{
string output = ""; //输出字符串
if (dosCommand != null && dosCommand != "")
{
Process process = new Process(); //创建进程对象
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe"; //设定需要执行的命令
startInfo.Arguments = "/C " + dosCommand; //设定参数,其中的“/C”表示执行完命令后马上退出
startInfo.UseShellExecute = false; //不使用系统外壳程序启动
startInfo.RedirectStandardInput = false; //不重定向输入
startInfo.RedirectStandardOutput = true; //重定向输出
startInfo.CreateNoWindow = true; //不创建窗口
process.StartInfo = startInfo;
try
{
if (process.Start())
{
if (milliseconds == 0)
{
process.WaitForExit(); //这里无限等待进程结束
}
else
{
process.WaitForExit(milliseconds); //这里等待进程结束,等待时间为指定的毫秒
output = process.StandardOutput.ReadToEnd();//读取进程的输出
}
}
}
catch (Exception)
{
throw;
}
finally
{
if (process != null)
process.Close();
}
}
return output;
}
///读
public static string GetValue(string Des)
{
XmlDocument xDoc = new XmlDocument();
//获取可执行文件的路径和名称
xDoc.Load("Config.xml");
XmlNode xNode;
XmlElement xElem1;
xNode = xDoc.SelectSingleNode("Settings");
xElem1 = (XmlElement)xNode.SelectSingleNode(Des);
if (xElem1 != null)
return xElem1.InnerText;
else
return "";
}
//写
public static void SetValue(string AppKey, string AppValue)
{
XmlDocument xDoc = new XmlDocument();
//获取可执行文件的路径和名称
xDoc.Load("Config.xml");
XmlNode xNode;
XmlElement xElem1;
XmlElement xElem2;
xNode = xDoc.SelectSingleNode("Settings");
xElem1 = (XmlElement)xNode.SelectSingleNode(AppKey);
if (xElem1 != null)
xElem1.InnerText = AppValue;
else
{
xElem2 = xDoc.CreateElement(AppKey);
xElem2.Value = AppValue;
xNode.AppendChild(xElem2);
}
xDoc.Save("Config.xml");
}
/// <summary>
/// 开机启动项
/// </summary>
/// <param name="Started">是否启动</param>
/// <param name="name">启动值的名称</param>
/// <param name="path">启动程序的路径</param>
public static void RunWhenStart(bool Started, string name, string path)
{
RegistryKey HKLM = Registry.LocalMachine;
RegistryKey Run = HKLM.CreateSubKey(@"SOFTWARE/Microsoft/Windows/CurrentVersion/Run");
if (Started == true)
{
try
{
Run.SetValue(name, path);
HKLM.Close();
}
catch (Exception Err)
{
System.Windows.Forms.MessageBox.Show(Err.Message, "ToolsByJack", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
else
{
try
{
if (Run.GetValue(name) != null)
{
//Run.DeleteKey(name, false);
Run.DeleteSubKey(name,false);
HKLM.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
private void 手动备份ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (MessageBox.Show("确实要执行备份功能么?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
Remote_Backup();
}
else
{
return;
}
}
/// <summary>
/// 使用WinRAR解压缩文件
/// </summary>
/// <param name="srcFilePath">要压缩的文件</param>
/// <param name="aimFilePath">保存压缩文件的路径</param>
/// <returns>压缩成功返回true,否则返回false</returns>
public static bool CreatRAR(string srcFilePath, string aimFilePath)
{
try
{
if (System.IO.File.Exists(aimFilePath))
System.IO.File.Delete(aimFilePath);
if (!System.IO.File.Exists(srcFilePath))
return false;
System.Diagnostics.Process ProcessRar = new System.Diagnostics.Process();
ProcessRar.StartInfo.FileName = "Winrar.exe";
ProcessRar.StartInfo.CreateNoWindow = true;
ProcessRar.StartInfo.Arguments = " a -r " + aimFilePath + " " + srcFilePath;
ProcessRar.Start();
ProcessRar.WaitForExit();
bool bln = false;
if (ProcessRar.HasExited)
if (ProcessRar.ExitCode == 0)
bln = true;
ProcessRar.Close();
return bln;
}
catch
{
return false;
}
}
/// <summary>
/// 使用WinRAR解压缩文件
/// </summary>
/// <param name="aimPath">解压文件存储的路径</param>
/// <param name="srcFilePath">压缩文件的完整地址</param>
/// <returns>解压完成返回true,否则返回false</returns>
public static bool UnRAR(string srcFilePath, string aimPath)
{
try
{
if (System.IO.File.Exists(srcFilePath))
{
string rarName = srcFilePath.Split('//')[srcFilePath.Split('//').Length - 1];
string rarPath = srcFilePath.Remove(srcFilePath.LastIndexOf(rarName) - 1);
Microsoft.Win32.RegistryKey regkey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(@"Applications/WinRAR.exe/shell/open/command");
System.Object regvalue = regkey.GetValue("");
string rarexe = regvalue.ToString();
regkey.Close();
rarexe = rarexe.Substring(1, rarexe.Length - 7);
if (!System.IO.File.Exists(aimPath))
System.IO.Directory.CreateDirectory(aimPath);
//解压缩命令,相当于在要压缩文件(rarName)上点右键->WinRAR->解压文件->目标路径(aimPath)
string cmd = string.Format("x {0} {1} -y ", rarName, aimPath);
System.Diagnostics.ProcessStartInfo startinfo = new System.Diagnostics.ProcessStartInfo();
startinfo.FileName = rarexe;
startinfo.Arguments = cmd;
startinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startinfo.WorkingDirectory = rarPath;
System.Diagnostics.Process ProcessRar = new System.Diagnostics.Process();
ProcessRar.StartInfo = startinfo;
ProcessRar.Start();
ProcessRar.WaitForExit();
bool bln = false;
if (ProcessRar.HasExited)
if (ProcessRar.ExitCode == 0)
bln = true;
ProcessRar.Close();
return bln;
}
else
{
return false;
}
}
catch
{
return false;
}
}
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
this.Visible = true;
this.WindowState = System.Windows.Forms.FormWindowState.Normal;
}
private void Main_FormClosing(object sender, FormClosingEventArgs e)
{
if (GetValue("Password") == "hengdadianqi")
{
//允许关闭
this.notifyIcon1.Visible = false;
e.Cancel = false;
}
else
{
e.Cancel = true;
}
}
private void 还原ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Visible = true;
this.WindowState = System.Windows.Forms.FormWindowState.Normal;
}
private void 关闭ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void Main_Load(object sender, EventArgs e)
{
//RunWhenStart(true, "Remote_Backup", "Remote_Backup_VS2005.exe");
this.Location = new Point(500,150);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (DateTime.Now.Hour.ToString() == GetValue("Hour") && DateTime.Now.Minute.ToString() == GetValue("Minute") && DateTime.Now.Second.ToString() == GetValue("Second"))
{
Remote_Backup();
}
}
//函数用于写日志文件
public static void WriteLog(string Content)
{
//FileInfo myFile = new FileInfo("filename");
//StreamWriter sw = myFile.CreateText();
//sw.Write(Content);
//sw.Close();
//文本文档写入内容
using (System.IO.StreamWriter sw = System.IO.File.AppendText(@"remote_backup.log"))
{
sw.WriteLine(DateTime.Now.ToString()+ Content);
sw.Dispose();
}
}
private void Main_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.ShowInTaskbar = false;
}
if (this.WindowState == FormWindowState.Normal)
{
this.ShowInTaskbar = true;
}
}
}
class FtpUpDown
{
string ftpServerIP;
string ftpUserID;
string ftpPassword;
FtpWebRequest reqFTP;
private void Connect(String path)//连接ftp
{
// 根据uri创建FtpWebRequest对象
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(path));
// 指定数据传输类型
reqFTP.UseBinary = true;
// ftp用户名和密码
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
}
public FtpUpDown(string ftpServerIP, string ftpUserID, string ftpPassword)
{
this.ftpServerIP = ftpServerIP;
this.ftpUserID = ftpUserID;
this.ftpPassword = ftpPassword;
}
//都调用这个
private string[] GetFileList(string path, string WRMethods)//上面的代码示例了如何从ftp服务器上获得文件列表
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
try
{
Connect(path);
reqFTP.Method = WRMethods;
WebResponse response = reqFTP.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.Default);//中文文件名
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("/n");
line = reader.ReadLine();
}
// to remove the trailing '/n'
result.Remove(result.ToString().LastIndexOf('/n'), 1);
reader.Close();
response.Close();
return result.ToString().Split('/n');
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
downloadFiles = null;
return downloadFiles;
}
}
public string[] GetFileList(string path)//上面的代码示例了如何从ftp服务器上获得文件列表
{
return GetFileList("ftp://" + ftpServerIP + "/" + path, WebRequestMethods.Ftp.ListDirectory);
}
public string[] GetFileList()//上面的代码示例了如何从ftp服务器上获得文件列表
{
return GetFileList("ftp://" + ftpServerIP + "/", WebRequestMethods.Ftp.ListDirectory);
}
///读
public static string GetValue(string Des)
{
XmlDocument xDoc = new XmlDocument();
//获取可执行文件的路径和名称
xDoc.Load(@"E:/近期的项目/VS2005重写的远程备份系统/Remote_Backup_VS2005/Remote_Backup_VS2005/bin/Debug/Config.xml");
XmlNode xNode;
XmlElement xElem1;
xNode = xDoc.SelectSingleNode("Settings");
xElem1 = (XmlElement)xNode.SelectSingleNode(Des);
if (xElem1 != null)
return xElem1.InnerText;
else
return "";
}
public void Upload(string filename, string Company) //上面的代码实现了从ftp服务器上载文件的功能
{
FileInfo fileInf = new FileInfo(filename);
string uri = "ftp://" + ftpServerIP + "/" + Company + "数据库备份/" + fileInf.Name;
//string uri = DesRoad;
Connect(uri);//连接
// 默认为true,连接不会被关闭
// 在一个命令之后被执行
reqFTP.KeepAlive = false;
// 指定执行什么命令
reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
// 上传文件时通知服务器文件的大小
reqFTP.ContentLength = fileInf.Length;
// 缓冲大小设置为kb
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
// 打开一个文件流(System.IO.FileStream) 去读上传的文件
FileStream fs = fileInf.OpenRead();
try
{
// 把上传的文件写入流
Stream strm = reqFTP.GetRequestStream();
// 每次读文件流的kb
contentLen = fs.Read(buff, 0, buffLength);
// 流内容没有结束
while (contentLen != 0)
{
// 把内容从file stream 写入upload stream
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}
// 关闭两个流
strm.Close();
fs.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message, "Upload Error");
}
}
public bool Download(string filePath, string fileName, out string errorinfo)////上面的代码实现了从ftp服务器下载文件的功能
{
try
{
String onlyFileName = Path.GetFileName(fileName);
string newFileName = filePath + "//" + onlyFileName;
if (File.Exists(newFileName))
{
errorinfo = string.Format("本地文件{0}已存在,无法下载", newFileName);
return false;
}
string url = "ftp://" + ftpServerIP + "/" + fileName;
Connect(url);//连接
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
long cl = response.ContentLength;
int bufferSize = 2048;
int readCount;
byte[] buffer = new byte[bufferSize];
readCount = ftpStream.Read(buffer, 0, bufferSize);
FileStream outputStream = new FileStream(newFileName, FileMode.Create);
while (readCount > 0)
{
outputStream.Write(buffer, 0, readCount);
readCount = ftpStream.Read(buffer, 0, bufferSize);
}
ftpStream.Close();
outputStream.Close();
response.Close();
errorinfo = "";
return true;
}
catch (Exception ex)
{
errorinfo = string.Format("因{0},无法下载", ex.Message);
return false;
}
}
//删除文件
public void DeleteFileName(string fileName)
{
try
{
FileInfo fileInf = new FileInfo(fileName);
string uri = "ftp://" + ftpServerIP + "/" + fileInf.Name;
Connect(uri);//连接
// 默认为true,连接不会被关闭
// 在一个命令之后被执行
reqFTP.KeepAlive = false;
// 指定执行什么命令
reqFTP.Method = WebRequestMethods.Ftp.DeleteFile;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message, "删除错误");
}
}
//创建目录
public void MakeDir(string dirName)
{
try
{
string uri = "ftp://" + ftpServerIP + "/" + dirName;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
//删除目录
public void delDir(string dirName)
{
try
{
string uri = "ftp://" + ftpServerIP + "/" + dirName;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.RemoveDirectory;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
//获得文件大小
public long GetFileSize(string filename)
{
long fileSize = 0;
try
{
FileInfo fileInf = new FileInfo(filename);
string uri = "ftp://" + ftpServerIP + "/" + fileInf.Name;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.GetFileSize;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
fileSize = response.ContentLength;
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return fileSize;
}
//文件改名
public void Rename(string currentFilename, string newFilename)
{
try
{
FileInfo fileInf = new FileInfo(currentFilename);
string uri = "ftp://" + ftpServerIP + "/" + fileInf.Name;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.Rename;
reqFTP.RenameTo = newFilename;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
//Stream ftpStream = response.GetResponseStream();
//ftpStream.Close();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
//获得文件明晰
public string[] GetFilesDetailList()
{
return GetFileList("ftp://" + ftpServerIP + "/", WebRequestMethods.Ftp.ListDirectoryDetails);
}
//获得文件明晰
public string[] GetFilesDetailList(string path)
{
return GetFileList("ftp://" + ftpServerIP + "/" + path, WebRequestMethods.Ftp.ListDirectoryDetails);
}
}
}
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Xml;
using Microsoft.Win32;
using System.IO;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Net;
namespace Remote_Backup_VS2005
{
[StructLayout(LayoutKind.Sequential)]
public struct NETRESOURCEA
{
public int dwScope;
public int dwType;
public int dwDisplayType;
public int dwUsage;
[MarshalAs(UnmanagedType.LPStr)]
public string lpLocalName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpRemoteName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpComment;
[MarshalAs(UnmanagedType.LPStr)]
public string lpProvider;
public override String ToString()
{
String str = "LocalName: " + lpLocalName + " RemoteName: " + lpRemoteName
+ " Comment: " + lpComment + " lpProvider: " + lpProvider;
return (str);
}
}
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
[System.Runtime.InteropServices.DllImport("mpr.dll ")]
public static extern int WNetAddConnection2A(
[MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource,
[MarshalAs(UnmanagedType.LPStr)] string lpPassword,
[MarshalAs(UnmanagedType.LPStr)] string UserName,
int dwFlags);
public static void Remote_Backup()
{
WriteLog("程序开始执行");
//使用FTP所以没有必要查看服务状态
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "server=.;database=cpudata;user id =sa;password=sa";
SqlCommand cmd = new SqlCommand();
cmd.CommandText = @"backup database cpudata to disk='c:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".bak' "; //更改为C盘
//cmd.CommandText = @"backup database cpudata to disk='c:/cpudata.bak'";
//backup database cpudata to disk ='c:/cpudata.bak'
cmd.Connection = conn;
//原来的时候需要记录文件个数对文件个数进行限制 现在不用了 所以程序注释掉
//SqlConnection conn_File_Limit = new SqlConnection();
//conn_File_Limit.ConnectionString = "server=" + GetValue("IP") + "," + GetValue("Port") + ";database=Check_Card_Database;user id =sa;password=19790601";
//SqlCommand command_Limite = new SqlCommand();
//command_Limite.Connection = conn_File_Limit;
//command_Limite.CommandText = "select count(ID) from Record where substring(Mark,1,4)='" + GetValue("ServerName") + "'";
//2011年5月9日19:37:00 修改程序全都压缩然后上传到服务器
try
{
conn.Open();
//conn_File_Limit.Open();
int i = cmd.ExecuteNonQuery(); //实现远程备份
if (i==-1)
{
WriteLog("备份数据库成功");
}
else
{
WriteLog("备份数据库失败");
}
//开始远程备份数据库流程 首先删除映射的磁盘
//Execute("net use * /delete /yes", 0);
string Company_Name = GetValue("ServerName").Trim().Substring(0,2);
//string NetUseCommand = @"net use z: //10.68.69.203/e$/各个供电公司数据库备份/东区数据库备份 yfyf /user:administrator";
//int result = DiskMap(Company_Name);
//if (result==0)
//{
// WriteLog("建立映射成功");
//}
//else if (result==85)
//{
// WriteLog("映射失败, 驱动器已经存在");
//}
//else if (result ==1202 || result ==1203)
//{
// WriteLog("映射失败,找不到网络路径");
//}
//else if (result == 1326)
//{
// WriteLog("映射失败,登录账号有误");
//}
//else
//{
// WriteLog("映射失败,可能有其他错误");
//}
//确认文件数量之后开始对文件进行压缩
CreatRAR("C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".bak", "C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar");
WriteLog("文件压缩成功");
Execute(@"del C:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".bak //yes", 0);
WriteLog("备份文件删除成功");
FtpUpDown ftpUpDown = new FtpUpDown("10.68.69.203", "administrator", "yfyf");
ftpUpDown.Upload("C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar", GetValue("ServerName").Trim().Substring(0, 2));
//string cmd_Copy_remote = @"copy C:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + @".rar z:/" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar";
// Execute(@"copy c:/cpudata.bak z:/cpudata.bak", 0);
//Execute(cmd_Copy_remote, 0);
WriteLog("文件传输成功");
//System.Threading.Thread.Sleep(5000);
Execute("del C://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar", 0);
WriteLog("压缩文件删除成功");
//文件备份完成之后要删除多出指定文件数量的文件
string File_Count = GetValue("File_Count"); //已经备份的文件数量
string File_Limit = GetValue("File_Limit"); //备份文件数量限制
// string path = @"Z:/";
//FileInfo[] files = new DirectoryInfo(path).GetFiles();
//List<FileInfo> list = new List<FileInfo>(files);
//list.Sort(new Comparison<FileInfo>(delegate(FileInfo a, FileInfo b)
//{
// return a.CreationTime.CompareTo(b.CreationTime);
//}));
//WriteLog("现在的文件数量是"+list.Count.ToString().Trim());
////首先判断文件数量和文件限制数量之间比较
//if (int.Parse(list.Count.ToString())<int.Parse(File_Limit))
//{
//}
//else
//{
// //等于就要删除最早的文件
// list[0].Delete();
// WriteLog("文件删除成功");
//}
//UnRAR("Z://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar", "Z://" + DateTime.Now.ToString("yyyy-MM-dd-HH"));
//System.Threading.Thread.Sleep(180000);
//Execute("del z://" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".rar",0);
//Execute("net use Z: /delete /yes", 0);
//WriteLog("映射删除成功");
//操作记录
//RecordLog();
//MessageBox.Show("Ok","提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
conn.Close();
//conn_File_Limit.Close();
}
}
//函数用于启动磁盘映射
public static int DiskMap(string Company)
{
NETRESOURCEA[] n = new NETRESOURCEA[1];
n[0] = new NETRESOURCEA();
n[0].dwType = 1;
int dwFlags = 1;
n[0].lpLocalName = @"Z:";
n[0].lpRemoteName = @"//10.68.69.203/e$/各个供电公司数据库备份/" + Company + "数据库备份";
n[0].lpProvider = null;
Console.WriteLine(n[0]);
int res = WNetAddConnection2A(n, "yfyf", "administrator", dwFlags);
return res;
}
/// <summary>
/// 执行DOS命令,返回DOS命令的输出
/// </summary>
/// <param name="dosCommand">dos命令</param>
/// <param name="milliseconds">等待命令执行的时间(单位:毫秒),如果设定为0,则无限等待</param>
/// <returns>返回输出,如果发生异常,返回空字符串</returns>
public static string Execute(string dosCommand, int milliseconds)
{
string output = ""; //输出字符串
if (dosCommand != null && dosCommand != "")
{
Process process = new Process(); //创建进程对象
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe"; //设定需要执行的命令
startInfo.Arguments = "/C " + dosCommand; //设定参数,其中的“/C”表示执行完命令后马上退出
startInfo.UseShellExecute = false; //不使用系统外壳程序启动
startInfo.RedirectStandardInput = false; //不重定向输入
startInfo.RedirectStandardOutput = true; //重定向输出
startInfo.CreateNoWindow = true; //不创建窗口
process.StartInfo = startInfo;
try
{
if (process.Start())
{
if (milliseconds == 0)
{
process.WaitForExit(); //这里无限等待进程结束
}
else
{
process.WaitForExit(milliseconds); //这里等待进程结束,等待时间为指定的毫秒
output = process.StandardOutput.ReadToEnd();//读取进程的输出
}
}
}
catch (Exception)
{
throw;
}
finally
{
if (process != null)
process.Close();
}
}
return output;
}
///读
public static string GetValue(string Des)
{
XmlDocument xDoc = new XmlDocument();
//获取可执行文件的路径和名称
xDoc.Load("Config.xml");
XmlNode xNode;
XmlElement xElem1;
xNode = xDoc.SelectSingleNode("Settings");
xElem1 = (XmlElement)xNode.SelectSingleNode(Des);
if (xElem1 != null)
return xElem1.InnerText;
else
return "";
}
//写
public static void SetValue(string AppKey, string AppValue)
{
XmlDocument xDoc = new XmlDocument();
//获取可执行文件的路径和名称
xDoc.Load("Config.xml");
XmlNode xNode;
XmlElement xElem1;
XmlElement xElem2;
xNode = xDoc.SelectSingleNode("Settings");
xElem1 = (XmlElement)xNode.SelectSingleNode(AppKey);
if (xElem1 != null)
xElem1.InnerText = AppValue;
else
{
xElem2 = xDoc.CreateElement(AppKey);
xElem2.Value = AppValue;
xNode.AppendChild(xElem2);
}
xDoc.Save("Config.xml");
}
/// <summary>
/// 开机启动项
/// </summary>
/// <param name="Started">是否启动</param>
/// <param name="name">启动值的名称</param>
/// <param name="path">启动程序的路径</param>
public static void RunWhenStart(bool Started, string name, string path)
{
RegistryKey HKLM = Registry.LocalMachine;
RegistryKey Run = HKLM.CreateSubKey(@"SOFTWARE/Microsoft/Windows/CurrentVersion/Run");
if (Started == true)
{
try
{
Run.SetValue(name, path);
HKLM.Close();
}
catch (Exception Err)
{
System.Windows.Forms.MessageBox.Show(Err.Message, "ToolsByJack", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
else
{
try
{
if (Run.GetValue(name) != null)
{
//Run.DeleteKey(name, false);
Run.DeleteSubKey(name,false);
HKLM.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
private void 手动备份ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (MessageBox.Show("确实要执行备份功能么?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
Remote_Backup();
}
else
{
return;
}
}
/// <summary>
/// 使用WinRAR解压缩文件
/// </summary>
/// <param name="srcFilePath">要压缩的文件</param>
/// <param name="aimFilePath">保存压缩文件的路径</param>
/// <returns>压缩成功返回true,否则返回false</returns>
public static bool CreatRAR(string srcFilePath, string aimFilePath)
{
try
{
if (System.IO.File.Exists(aimFilePath))
System.IO.File.Delete(aimFilePath);
if (!System.IO.File.Exists(srcFilePath))
return false;
System.Diagnostics.Process ProcessRar = new System.Diagnostics.Process();
ProcessRar.StartInfo.FileName = "Winrar.exe";
ProcessRar.StartInfo.CreateNoWindow = true;
ProcessRar.StartInfo.Arguments = " a -r " + aimFilePath + " " + srcFilePath;
ProcessRar.Start();
ProcessRar.WaitForExit();
bool bln = false;
if (ProcessRar.HasExited)
if (ProcessRar.ExitCode == 0)
bln = true;
ProcessRar.Close();
return bln;
}
catch
{
return false;
}
}
/// <summary>
/// 使用WinRAR解压缩文件
/// </summary>
/// <param name="aimPath">解压文件存储的路径</param>
/// <param name="srcFilePath">压缩文件的完整地址</param>
/// <returns>解压完成返回true,否则返回false</returns>
public static bool UnRAR(string srcFilePath, string aimPath)
{
try
{
if (System.IO.File.Exists(srcFilePath))
{
string rarName = srcFilePath.Split('//')[srcFilePath.Split('//').Length - 1];
string rarPath = srcFilePath.Remove(srcFilePath.LastIndexOf(rarName) - 1);
Microsoft.Win32.RegistryKey regkey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(@"Applications/WinRAR.exe/shell/open/command");
System.Object regvalue = regkey.GetValue("");
string rarexe = regvalue.ToString();
regkey.Close();
rarexe = rarexe.Substring(1, rarexe.Length - 7);
if (!System.IO.File.Exists(aimPath))
System.IO.Directory.CreateDirectory(aimPath);
//解压缩命令,相当于在要压缩文件(rarName)上点右键->WinRAR->解压文件->目标路径(aimPath)
string cmd = string.Format("x {0} {1} -y ", rarName, aimPath);
System.Diagnostics.ProcessStartInfo startinfo = new System.Diagnostics.ProcessStartInfo();
startinfo.FileName = rarexe;
startinfo.Arguments = cmd;
startinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startinfo.WorkingDirectory = rarPath;
System.Diagnostics.Process ProcessRar = new System.Diagnostics.Process();
ProcessRar.StartInfo = startinfo;
ProcessRar.Start();
ProcessRar.WaitForExit();
bool bln = false;
if (ProcessRar.HasExited)
if (ProcessRar.ExitCode == 0)
bln = true;
ProcessRar.Close();
return bln;
}
else
{
return false;
}
}
catch
{
return false;
}
}
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
this.Visible = true;
this.WindowState = System.Windows.Forms.FormWindowState.Normal;
}
private void Main_FormClosing(object sender, FormClosingEventArgs e)
{
if (GetValue("Password") == "hengdadianqi")
{
//允许关闭
this.notifyIcon1.Visible = false;
e.Cancel = false;
}
else
{
e.Cancel = true;
}
}
private void 还原ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Visible = true;
this.WindowState = System.Windows.Forms.FormWindowState.Normal;
}
private void 关闭ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void Main_Load(object sender, EventArgs e)
{
//RunWhenStart(true, "Remote_Backup", "Remote_Backup_VS2005.exe");
this.Location = new Point(500,150);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (DateTime.Now.Hour.ToString() == GetValue("Hour") && DateTime.Now.Minute.ToString() == GetValue("Minute") && DateTime.Now.Second.ToString() == GetValue("Second"))
{
Remote_Backup();
}
}
//函数用于写日志文件
public static void WriteLog(string Content)
{
//FileInfo myFile = new FileInfo("filename");
//StreamWriter sw = myFile.CreateText();
//sw.Write(Content);
//sw.Close();
//文本文档写入内容
using (System.IO.StreamWriter sw = System.IO.File.AppendText(@"remote_backup.log"))
{
sw.WriteLine(DateTime.Now.ToString()+ Content);
sw.Dispose();
}
}
private void Main_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.ShowInTaskbar = false;
}
if (this.WindowState == FormWindowState.Normal)
{
this.ShowInTaskbar = true;
}
}
}
class FtpUpDown
{
string ftpServerIP;
string ftpUserID;
string ftpPassword;
FtpWebRequest reqFTP;
private void Connect(String path)//连接ftp
{
// 根据uri创建FtpWebRequest对象
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(path));
// 指定数据传输类型
reqFTP.UseBinary = true;
// ftp用户名和密码
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
}
public FtpUpDown(string ftpServerIP, string ftpUserID, string ftpPassword)
{
this.ftpServerIP = ftpServerIP;
this.ftpUserID = ftpUserID;
this.ftpPassword = ftpPassword;
}
//都调用这个
private string[] GetFileList(string path, string WRMethods)//上面的代码示例了如何从ftp服务器上获得文件列表
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
try
{
Connect(path);
reqFTP.Method = WRMethods;
WebResponse response = reqFTP.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.Default);//中文文件名
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("/n");
line = reader.ReadLine();
}
// to remove the trailing '/n'
result.Remove(result.ToString().LastIndexOf('/n'), 1);
reader.Close();
response.Close();
return result.ToString().Split('/n');
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
downloadFiles = null;
return downloadFiles;
}
}
public string[] GetFileList(string path)//上面的代码示例了如何从ftp服务器上获得文件列表
{
return GetFileList("ftp://" + ftpServerIP + "/" + path, WebRequestMethods.Ftp.ListDirectory);
}
public string[] GetFileList()//上面的代码示例了如何从ftp服务器上获得文件列表
{
return GetFileList("ftp://" + ftpServerIP + "/", WebRequestMethods.Ftp.ListDirectory);
}
///读
public static string GetValue(string Des)
{
XmlDocument xDoc = new XmlDocument();
//获取可执行文件的路径和名称
xDoc.Load(@"E:/近期的项目/VS2005重写的远程备份系统/Remote_Backup_VS2005/Remote_Backup_VS2005/bin/Debug/Config.xml");
XmlNode xNode;
XmlElement xElem1;
xNode = xDoc.SelectSingleNode("Settings");
xElem1 = (XmlElement)xNode.SelectSingleNode(Des);
if (xElem1 != null)
return xElem1.InnerText;
else
return "";
}
public void Upload(string filename, string Company) //上面的代码实现了从ftp服务器上载文件的功能
{
FileInfo fileInf = new FileInfo(filename);
string uri = "ftp://" + ftpServerIP + "/" + Company + "数据库备份/" + fileInf.Name;
//string uri = DesRoad;
Connect(uri);//连接
// 默认为true,连接不会被关闭
// 在一个命令之后被执行
reqFTP.KeepAlive = false;
// 指定执行什么命令
reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
// 上传文件时通知服务器文件的大小
reqFTP.ContentLength = fileInf.Length;
// 缓冲大小设置为kb
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
// 打开一个文件流(System.IO.FileStream) 去读上传的文件
FileStream fs = fileInf.OpenRead();
try
{
// 把上传的文件写入流
Stream strm = reqFTP.GetRequestStream();
// 每次读文件流的kb
contentLen = fs.Read(buff, 0, buffLength);
// 流内容没有结束
while (contentLen != 0)
{
// 把内容从file stream 写入upload stream
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}
// 关闭两个流
strm.Close();
fs.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message, "Upload Error");
}
}
public bool Download(string filePath, string fileName, out string errorinfo)////上面的代码实现了从ftp服务器下载文件的功能
{
try
{
String onlyFileName = Path.GetFileName(fileName);
string newFileName = filePath + "//" + onlyFileName;
if (File.Exists(newFileName))
{
errorinfo = string.Format("本地文件{0}已存在,无法下载", newFileName);
return false;
}
string url = "ftp://" + ftpServerIP + "/" + fileName;
Connect(url);//连接
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
long cl = response.ContentLength;
int bufferSize = 2048;
int readCount;
byte[] buffer = new byte[bufferSize];
readCount = ftpStream.Read(buffer, 0, bufferSize);
FileStream outputStream = new FileStream(newFileName, FileMode.Create);
while (readCount > 0)
{
outputStream.Write(buffer, 0, readCount);
readCount = ftpStream.Read(buffer, 0, bufferSize);
}
ftpStream.Close();
outputStream.Close();
response.Close();
errorinfo = "";
return true;
}
catch (Exception ex)
{
errorinfo = string.Format("因{0},无法下载", ex.Message);
return false;
}
}
//删除文件
public void DeleteFileName(string fileName)
{
try
{
FileInfo fileInf = new FileInfo(fileName);
string uri = "ftp://" + ftpServerIP + "/" + fileInf.Name;
Connect(uri);//连接
// 默认为true,连接不会被关闭
// 在一个命令之后被执行
reqFTP.KeepAlive = false;
// 指定执行什么命令
reqFTP.Method = WebRequestMethods.Ftp.DeleteFile;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message, "删除错误");
}
}
//创建目录
public void MakeDir(string dirName)
{
try
{
string uri = "ftp://" + ftpServerIP + "/" + dirName;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
//删除目录
public void delDir(string dirName)
{
try
{
string uri = "ftp://" + ftpServerIP + "/" + dirName;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.RemoveDirectory;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
//获得文件大小
public long GetFileSize(string filename)
{
long fileSize = 0;
try
{
FileInfo fileInf = new FileInfo(filename);
string uri = "ftp://" + ftpServerIP + "/" + fileInf.Name;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.GetFileSize;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
fileSize = response.ContentLength;
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return fileSize;
}
//文件改名
public void Rename(string currentFilename, string newFilename)
{
try
{
FileInfo fileInf = new FileInfo(currentFilename);
string uri = "ftp://" + ftpServerIP + "/" + fileInf.Name;
Connect(uri);//连接
reqFTP.Method = WebRequestMethods.Ftp.Rename;
reqFTP.RenameTo = newFilename;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
//Stream ftpStream = response.GetResponseStream();
//ftpStream.Close();
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
//获得文件明晰
public string[] GetFilesDetailList()
{
return GetFileList("ftp://" + ftpServerIP + "/", WebRequestMethods.Ftp.ListDirectoryDetails);
}
//获得文件明晰
public string[] GetFilesDetailList(string path)
{
return GetFileList("ftp://" + ftpServerIP + "/" + path, WebRequestMethods.Ftp.ListDirectoryDetails);
}
}
}
484

被折叠的 条评论
为什么被折叠?



