上篇博文讲解了一下IOS文件的上传,下面来说一下文件的下载(对于文件的上传与下载在客户端有些代码是重复的)
首先说一下文件下载的服务端代码,有多种方法可以实现文件的下载,如下:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;
namespace webdownloadupload
{
/// <summary>
/// 处理文件的下载 downloadfile.ashx
/// </summary>
public class DownloadFileHandler : IHttpHandler
{
public string DOWNLOAD_PATH = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["DownloadPath"]);
private const int BUFFER_LEN = 8192;
//建议使用HandlerHeaderDownLoadFile或者HandlerDownLoadFileThree方式实现下载
public void ProcessRequest(HttpContext context)
{
string filename = context.Request.Params["filename"];
if (String.IsNullOrEmpty(filename))
{
filename = context.Request.Headers["filename"];
if (String.IsNullOrEmpty(filename))
{
long len = context.Request.InputStream.Length;
byte[] b = new byte[len];
//可以自定义数据结构来这里实现数据的解析
context.Request.InputStream.Read(b, 0, b.Length);
String result = System.Text.Encoding.Default.GetString(b);
//在客户端以数据流的方式传递过来
JavaScriptSerializer Serializer = new JavaScriptSerializer();
Dictionary<Object,Object> obj = Serializer.Deserialize<Dictionary<object, object>>(result);
foreach (object o in obj.Keys)
{
context.Request.Headers.Add(o.ToString(), obj[o].ToString());
//context.Request.Form.Add(o.ToString(), obj[o].ToString());//只读的
//context.Request.Params.Add(o.ToString(), obj[o].ToString());//只读的
}
HandlerHeaderDownLoadFile(context);
}
else
{
HandlerHeaderDownLoadFile(context);
}
}
else
{
HandlerParamsDownLoadFile(context);
}
}
//NSFileHandler
private void HandlerHeaderDownLoadFile(HttpContext context)
{
long len = context.Request.InputStream.Length;
byte[] b = new byte[len];
context.Request.InputStream.Read(b, 0, b.Length);
String result = System.Text.Encoding.Default.GetString(b);
context.Response.Clear();
context.Response.AddHeader(@"content-type", @"application/octet-stream");
string filename = context.Request.Headers["filename"];
if (String.IsNullOrEmpty(filename))
{
context.Response.Write("{\"result\":\"failed\",\"message\":\"filename is null\"}");
context.Response.End();
return;
}
context.Response.AddHeader("Content-Disposition", "attachement;filename=" + filename);
context.Response.AddHeader("filename", filename);
String fullfilename = String.Format("{0}{1}", DOWNLOAD_PATH, filename);
byte[] buffer = new byte[BUFFER_LEN];
try
{
using (FileStream fs = new FileStream(fullfilename, FileMode.Open, FileAccess.Read, FileShare.None))
{
context.Response.AddHeader(@"Content-Length", fs.Length.ToString());
int length = 0;
while ((length = fs.Read(buffer, 0, BUFFER_LEN)) > 0)
{
context.Response.OutputStream.Write(buffer, 0, length);
}
}
}
catch (Exception ex)
{
context.Response.Write("{\"result\":\"failed\",\"message\":\""+ex.Message+"\"}");
context.Response.End();
}
context.Response.Write("{\"result\":\"success\",\"message\":\"\"}");
context.Response.Flush();
context.Response.End();
}
/// <summary>
/// 写不同的方法只是为了测试下,个人建议使用HandlerDownLoadFileThree方式下载
/// </summary>
/// <param name="context"></param>
private void HandlerParamsDownLoadFile(HttpContext context)
{
int downloadtype = Convert.ToInt32(context.Request.Params["downloadtype"]);
switch (downloadtype)
{
case 0:
HandlerDownLoadFileZero(context);
break;
case 1:
HandlerDownLoadFileOne(context);
break;
case 2:
HandlerDownLoadFileTwo(context);
break;
case 3:
case 5:
HandlerDownLoadFileThree(context);
break;
case 4:
HandlerDownLoadFileFour(context);
break;
default:
break;
}
}
/// <summary>
/// 之前一直用这种下载方式,可是有一次用户上传了一个700Mb的文件时报内存溢出的问题,
/// 分析了一下原因,用户的内存只有256M,而下载文件时要创建内存流,导致了内存溢出。
/// </summary>
/// <param name="context"></param>
private void HandlerDownLoadFileZero(HttpContext context)
{
string filename = context.Request.Params["filename"];
context.Response.ClearHeaders();
context.Response.Clear();
context.Response.Expires = 0;
context.Response.Buffer = true;
String path = String.Format("{0}{1}",DOWNLOAD_PATH,filename);
FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
byte[] bytes = null;
if (stream.Length == 0)
{
bytes = new byte[1];
}
else {
bytes = new byte[stream.Length];
}
stream.Read(bytes, 0, (int)bytes.Length);
stream.Close();
context.Response.AddHeader("Content-Disposition", "attachment;file=" + filename);
context.Response.ContentType = "application/octet-stream;charset=utf-8";
context.Response.BinaryWrite(bytes);
context.Response.End();
}
/// <summary>
/// TransmitFile 实现下载
/// </summary>
/// <param name="context"></param>
private void HandlerDownLoadFileOne(HttpContext context)
{
/*
微软为Response对象提供了一个新的方法TransmitFile来解决使用Response.BinaryWrite
下载超过400mb的文件时导致Aspnet_wp.exe进程回收而无法成功下载的问题。
代码如下:
*/
context.Response.AddHeader("Content-Type", "z-rar-compressed");
string filename = context.Request.Params["filename"];
context.Response.AddHeader("Content-Disposition", "attachment;filename="+filename);
string filepath = string.Format("{0}{1}", DOWNLOAD_PATH, filename);
context.Response.TransmitFile(filepath);
}
/// <summary>
/// WriteFile下载
/// </summary>
/// <param name="context"></param>
private void HandlerDownLoadFileTwo(HttpContext context)
{
string filename = context.Request.Params["filename"];
string filepath = string.Format("{0}{1}", DOWNLOAD_PATH, filename);
FileInfo fileinfo = new FileInfo(filepath);
context.Response.Clear();
context.Response.ClearContent();
context.Response.ClearHeaders();
context.Response.AddHeader("Content-Disposition", "attachment;filename="+filename);
context.Response.AddHeader("Content-Length", fileinfo.Length.ToString());
context.Response.AddHeader("Content-Transfer-Encoding", "binary");
context.Response.AddHeader("Content-Type", "application/octet-stream");
context.Response.ContentEncoding = System.Text.Encoding.GetEncoding("utf-8");
context.Response.WriteFile(fileinfo.FullName);
context.Response.Flush();
context.Response.End();
}
/// <summary>
/// WriteFile分块下载
/// </summary>
/// <param name="context"></param>
private void HandlerDownLoadFileThree(HttpContext context)
{
string filename = context.Request.Params["filename"];
string filepath = string.Format("{0}{1}", DOWNLOAD_PATH, filename);
FileInfo fileinfo = new FileInfo(filepath);
if (fileinfo.Exists)
{
const long chunksize = 102400;//每次读取100K
byte[] buffer = new byte[chunksize];
context.Response.Clear();
FileStream stream = File.OpenRead(filepath);
long dataLengthToRead = stream.Length;//获取下载文件的大小
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
while (dataLengthToRead > 0 && context.Response.IsClientConnected)
{
int lengthRead = stream.Read(buffer, 0, Convert.ToInt32(chunksize));
context.Response.OutputStream.Write(buffer, 0, lengthRead);
context.Response.Flush();
dataLengthToRead = dataLengthToRead - lengthRead;
}
}
context.Response.Close();
}
/// <summary>
/// 流方式下载
/// </summary>
/// <param name="context"></param>
private void HandlerDownLoadFileFour(HttpContext context)
{
string filename = context.Request.Params["filename"];
string filepath = string.Format("{0}{1}", DOWNLOAD_PATH, filename);
FileStream fs = new FileStream(filepath, FileMode.Open);
byte[] bytes = new byte[(int)fs.Length];
fs.Read(bytes, 0, bytes.Length);
fs.Close();
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("Content-Disposition", "attachment;filename =" + filename);
context.Response.BinaryWrite(bytes);
context.Response.Flush();
context.Response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
以后就是服务端代码的全部,写的不完善的地方,欢迎大家批评指正,下面来说可以端的代码,如下图:
#pragma mark 生成唯一的文件名
-(NSString *)GenerateFileName{
int seed = arc4random();
NSDate *date =[NSDate date];
NSTimeInterval sec =[date timeIntervalSinceNow];
NSDate *currentDate =[[NSDate alloc] initWithTimeIntervalSinceNow:sec];
NSDateFormatter *df =[[NSDateFormatter alloc] init];
[df setDateFormat:@"yyyyMMddHHmmsssss"];
NSString *fileName =[NSString stringWithFormat:@"%@%d%@",[df stringFromDate:currentDate],seed,@".rar"];
return fileName;
}
#pragma mark 获取一个随机数,范围[from,to)
-(int)getRandomNumber:(int)from to:(int)to{
return (int)(from +(arc4random()%(to-from+1)));
}
#pragma mark 下载文件一
-(void)BtnDownLoadFileMannerOne:(id)sender{
NSString *targetfilename = [self GenerateFileName];
int index = [self getRandomNumber:0 to:5];
NSString *sourcefilenaame = [self.sourceArray objectAtIndex:index];
//通过GET方式传递参数
NSString *downloadurlparams = [downloadurl stringByAppendingFormat:@"?filename=%@&downloadtype=%d",sourcefilenaame,index];
NSURL *UrlServer =[NSURL URLWithString:downloadurlparams];
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:UrlServer];
[request setHTTPMethod:@"GET"];
[request setValue:sourcefilenaame forHTTPHeaderField:@"filename"];
[request setHTTPMethod:@"POST"];
NSString *postMsg =[NSString stringWithFormat:@"message=%@",@"hello"];
NSData *postData =[postMsg dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
[request setHTTPBody:postData];
FileUploadDelegate *delegate =[[FileUploadDelegate alloc] init];
delegate.direction = TRANSFER_DOWNLOAD;
NSArray *pathArray =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
delegate.target =[[pathArray objectAtIndex:0] stringByAppendingPathComponent:targetfilename];
delegate.connection = [NSURLConnection connectionWithRequest:request delegate:delegate];
}
#pragma mark 下载文件二
-(void)BtnDownLoadFileMannerTwo:(id)sender{
//POST方式传递参数
NSString *targetfilename = [self GenerateFileName];
int index = [self getRandomNumber:0 to:5];
NSString *sourcefilenaame = [self.sourceArray objectAtIndex:index];
NSURL *UrlServer =[NSURL URLWithString:downloadurl];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:UrlServer];
[request setHTTPMethod:@"POST"];
NSString *postMsg =[NSString stringWithFormat:@"filename=%@&downloadtype=%d",sourcefilenaame,index];
NSData *postData = [postMsg dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
[request setHTTPBody:postData];
FileUploadDelegate *delegate =[[FileUploadDelegate alloc] init];
delegate.direction = TRANSFER_DOWNLOAD;
NSArray *pathArray =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
delegate.target =[[pathArray objectAtIndex:0] stringByAppendingPathComponent:targetfilename];
delegate.connection = [NSURLConnection connectionWithRequest:request delegate:delegate];
}
#pragma mark 下载文件三
-(void)BtnDownLoadFileMannerThree:(id)sender{
NSString *targetfilename = [self GenerateFileName];
int index = [self getRandomNumber:0 to:5];
NSString *sourcefilenaame = [self.sourceArray objectAtIndex:index];
NSURL *UrlServer =[NSURL URLWithString:downloadurl];
//GET 通过Header传递参数
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:UrlServer];
[request setValue:sourcefilenaame forHTTPHeaderField:@"filename"];
[request setValue:[NSString stringWithFormat:@"%d",index] forHTTPHeaderField:@"downloadtype"];
FileUploadDelegate *delegate =[[FileUploadDelegate alloc] init];
delegate.direction = TRANSFER_DOWNLOAD;
NSArray *pathArray =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
delegate.target =[[pathArray objectAtIndex:0] stringByAppendingPathComponent:targetfilename];
delegate.connection = [NSURLConnection connectionWithRequest:request delegate:delegate];
}
#pragma mark 下载文件四
-(void)BtnDownLoadFileMannerFour:(id)sender{
NSString *targetfilename = [self GenerateFileName];
int index = [self getRandomNumber:0 to:5];
NSString *sourcefilenaame = [self.sourceArray objectAtIndex:index];
NSURL *UrlServer =[NSURL URLWithString:downloadurl];
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:UrlServer];
//POST 方式,转换为Array转换为JSON字符串
NSMutableDictionary *dic =[NSMutableDictionary dictionary];
[dic setObject:sourcefilenaame forKey:@"filename"];
[dic setObject:[NSString stringWithFormat:@"%d",index] forKey:@"downloadtype"];
NSString *httpBodyString = [[[SBJson4Writer alloc] init] stringWithObject:dic];
NSData *bodyData =[httpBodyString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:bodyData];
FileUploadDelegate *delegate =[[FileUploadDelegate alloc] init];
delegate.direction = TRANSFER_DOWNLOAD;
NSArray *pathArray =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
delegate.target =[[pathArray objectAtIndex:0] stringByAppendingPathComponent:targetfilename];
delegate.connection = [NSURLConnection connectionWithRequest:request delegate:delegate];
}
FileUploadDElegate的代码看上篇博文,这个不在重复