这里简单的介绍一种从http获取文件然后下载到本地的方法,开始我用单线程下载,文件多的情况下速度太慢了,后来就采用多线程,这里琢磨了好久才整出来一个。这里的部分代码是在博客园找到的,具体是在哪里不是很清楚了,搜索关键词大概是http下载文件,感谢下。
1.关于多线程的思路,文件下载线程,日志记录线程(使用txt记录下载文件日志),UI线程。每个线程独立运行,互不干扰。
(1)这里说明下为什么要给UI单独一个线程,主要是因为文件数量太多的时候,如果这个线程是和文件下载线程和在一起的时候,我稍微一动下winform的界面,程序就会卡死,直到程序结束,我表示我已经注销了好多次电脑了。
(2)关于日志记录线程,因为文件下载是多线程,我这里就不能把日志记录在文件下载线程里面,不然几个线程一起占用txt的文件读写。这里会打架的。
(3)关于文件下载线程,这里比较简单了,我启用了十个线程做文件下载,每个文件下载单独的使用一个线程,线程完了之后继续下一个任务进行下载。这里我测试了下,十个线程下载速度还是比较理想的,内存使用也不是很大,但是这样可能很占用服务器IO内存,还是要稍微注意点的,线程不要太多了,影响网站的访问就不是很好了。
不说了 贴代码
private bool flag = true;//进程标识符
private int n = 0;//进程记录数量
private List<string> list=new List<string>(); //日志记录临时表
//初始化代码
public HttpDownloadFile()
{
InitializeComponent();
HttpDownloadFile.CheckForIllegalCrossThreadCalls = false;//这里必须设置,忘记是做啥用的,大家用的时候可以先不设置,看是报什么错误先
}
点击按钮进行文件下载
private void button1_Click(object sender, EventArgs e)
{
Thread th = new Thread(main);
th.Start();//启动文件下载主线程
Thread thtxt = new Thread(writeTxt);
thtxt.Start();//启动日志记录线程
}
日志线程函数说明:
protected void writeTxt() {
while (true)
{
List<string> templist = new List<string>(list);//将list复制到templist
foreach (string msg in templist)
{
string transferFile = textBox3.Text.Trim();
DateTime dt = DateTime.Now;
string str;
str = msg;
StreamWriter sw = new StreamWriter(transferFile + dt.Year + "" + dt.Month + "" + dt.Day + "log.txt", true);
sw.WriteLine(dt.ToString() + ":" + str);
sw.Close();//写入
list.Remove(msg);
}
templist = new List<string>();
}
}
文件下载主线程:
private void main() {
DateTime dt = DateTime.Now;//开始接收时间
label5.Text = dt.ToString();
List<string> list = new List<string>();
DataSet ds = new DataSet();
//从数据库获取文件路径
if (checkBox1.Checked)
{
ds = Dal.DalDatebase.getUrl(textBox2.Text.Trim());
}
else {
ds = Dal.DalDatebase.getUrl(textBox2.Text.Trim(),1);
}
DataRowCollection drc = ds.Tables[0].Rows;
textBox4.Text = drc.Count.ToString();
int i = 0;
string transferFile = textBox3.Text.Trim();
while (flag)
{
if (i == drc.Count) {
flag = false;
}
if (n < 10)
{
string tempurl = drc[i]["url"].ToString();
string fileName = tempurl.Split('/')[tempurl.Split('/').Length - 1];
ParameterizedThreadStart hbStart = new ParameterizedThreadStart(GetFile);
Thread hbth = new Thread(hbStart);
conData con = new conData();
con.fileName = fileName;
con.i = i;
con.transferFile = transferFile;
con.url = tempurl;
object ur = con;
hbth.Start(ur);
n++;//线程数
i++;//数据库获取的数据增量
}
}
if (n == 0)//线程结束之后弹出文件接收完毕
{
MessageBox.Show("文件接收完毕");
dt = DateTime.Now;//开始接收时间
label5.Text = dt.ToString();
}
}
下载文件代码:
private void GetFile(object str)
{
conData con = (conData)str;
try
{
listBox1.Items.Add("文件(" + con.i + "):" + con.fileName + "开始接收");
FileStream fs = new FileStream(con.transferFile + con.fileName, System.IO.FileMode.Create);
try
{
HttpWebRequest request= (HttpWebRequest)HttpWebRequest.Create(con.url);
long filesize = request.GetResponse().ContentLength;//取得目标文件的长度
if (filesize > 0)
{
byte[] nbytes = new byte[512];
System.IO.Stream ns = request.GetResponse().GetResponseStream();//获得接收流
int nreadsize = ns.Read(nbytes, 0, 512);
while (nreadsize > 0)
{
fs.Write(nbytes, 0, nreadsize);
nreadsize = ns.Read(nbytes, 0, 512);
}
fs.Close();
ns.Close();
}
request.Abort();
}
catch (Exception er)
{
listBox1.Items.Add("文件(" + con.i + "):" + con.fileName + "接收失败,错误信息:"+er.Message);
fs.Close();
}
listBox1.Items.Add("文件(" + con.i + "):" + con.fileName + "接收完毕!");
listBox1.SelectedIndex = listBox1.Items.Count - 1;
list.Add("文件(" + con.i + "):" + con.fileName + "接收完毕!");
}
catch (Exception e)
{
list.Add("文件(" + con.i + "):" + con.fileName + "接收失败!错误信息:" + e.Message);
}
n--;
}
多线程传参的时候是对象形式,我把多个对象写成了一个class,方便传参
public class conData {
public string transferFile { set; get; }
public string url { set; get; }
public string fileName { set; get; }
public int i { set; get; }
}
private void button2_Click(object sender, EventArgs e)
{
flag = false;//原有的线程继续运行,不会增加新的进程
this.Close();
}
private void button3_Click(object sender, EventArgs e)
{
Dispose();
System.Environment.Exit(0); //结束所有线程
this.Close();
}
好了 也在这里代码也基本结束了。如果大家有什么疑问或者有什么改进的意见,欢迎留言。做就要做的更好。