HTTP文件上传
看到网上很多链接文件(word、pdf...)可以下载,想制作http下载链接。
其实是将某文件直接放在服务器上搭建的网站上某目录下即可,例如:http://xxx:port/UpgradePack/525.jpg 或者 http://xxx:port//博客.pdf。
http服务器有很多种, 如tomcat,apache,IIS等,也可以直接下载http服务器hfs(http file server)快速搭建。
一、利用C#模拟文件上传至服务器
主要有以下三种基本方法:
方法一:用Web控件FileUpload
上传到网站根目录,适用于asp.net webForm
<form id="form1" runat="server"> <%--上传文件大小有限制--%> <asp:FileUpload ID="FileUpload1" AllowMultiple="true" runat="server" /> <br /> <br /> <%--直接进FileUploadTest.aspx.cs中处理--%> <asp:Button ID="Button1" runat="server" Text="上传到服务器" OnClick="Button1_Click" /> <asp:Label ID="Label1" runat="server" Text="" Style="color: Red"></asp:Label> </form> protected void Button1_Click(object sender, EventArgs e) { //处理单个文件 //if(FileUpload1.HasFile) //{ // FileUpload1.SaveAs(Server.MapPath("~/Files/") + FileUpload1.FileName); // Label1.Text = "上传成功"; //} //else //{ // Label1.Text = "上传失败"; //} if (FileUpload1.HasFile) { foreach (var item in FileUpload1.PostedFiles) { FileUpload1.SaveAs(Server.MapPath("~/Files/") + item.FileName); } Label1.Text = "上传成功"; } else { Label1.Text = "上传失败"; } }
允许一次选择多个文件:AllowMultiple="true"
注意:FileUpLoad上传控件,限制文件上传大小默认4096kb(4MB)
如果我们要上传超过此大小的文件,会出现错误界面等……
如果想增大允许上传的文件大小,则需要修改web.config文件代码:
<configuration> <system.web> <httpRuntime maxRequestLength="4096" executionTimeout="120"/> </system.web> </configuration>
说明:
maxRequestLength属性限制文件上传的大小,是以KB为单位的,默认值为4096KB,而最大上限为2097151KB,大约是2GB。
executionTimeout属性限制文件上传的时间,以秒(s)为单位,默认值为90 s,如果您考虑到所设计的Web应用系统上传时间要超过90 s可延长设定值。
方法二:用Html控件HtmlInputFile
<form id="form1" runat="server"> <input type="file" id="file1" runat="server" /> <asp:Button ID="Button1" runat="server" Text="上传" OnClick="Button1_Click" /> <asp:Label ID="Label1" runat="server" Text="" Style="color: Red"></asp:Label> </form> protected void Button1_Click(object sender, EventArgs e) { if (file1.PostedFile.ContentLength > 0) { file1.PostedFile.SaveAs(Server.MapPath("~/") + Path.GetFileName(file1.PostedFile.FileName)); Label1.Text = "上传成功!"; } }
选择多个文件:multiple="multiple" ,但是发现后端没有获取文件列表的方法/属性。不知道是不是这个HtmlInputFile只能单文件上传,有哪位清楚,还望告知下。
注意两个区别:
一:FileUpload.FileName获取客户端上传文件名(不带路径),而file1.PostedFile.FileName 和Request.Files["file"].FileName在不同浏览器下情况不同:IE8下获得的是客户端上传文件的完全限定名(带路径),谷歌、苹果等浏览器下则仍为文件名(不带路径)。
二:FileUpload控件有HasFile属性,用于判断用户是否选择了上传文件,而后面两种方法则需要通过判断上传文件大小ContentLength属性,当用户没有选择上传文件时,该属性值为0。
可以看出FileUpload封装程度更高,但灵活性也稍差。
方法三:Html元素input type="file"
- 通过表单提交,获取Request.Files
<%-- enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。[在使用包含文件上传控件的表单时,必须使用multipart/form-data。]--%> <form id="form1" runat="server" enctype="multipart/form-data"> <input type="file" name="file" multiple="multiple" /> <br /> <br /> <input type="text" name="author" value="huy" /> <asp:Button ID="Button1" runat="server" Text="上传到服务器" OnClick="Button1_Click" /> <asp:Label ID="Label1" runat="server" Text="" Style="color: Red"></asp:Label> </form> protected void Button1_Click(object sender, EventArgs e) { string name = Request.Form["author"]; Response.Write(name); HttpFileCollection files = Request.Files; if (files.Count > 0) { for (int i = 0; i < files.Count; i++) { files[i].SaveAs(Server.MapPath("~/Files/") + Path.GetFileName(files[i].FileName)); } Label1.Text = "上传成功"; } else { Label1.Text = "上传失败"; } }
注意:form需要设置,enctype="multipart/form-data"。。多文件上传(multiple="multiple") HTML5中支持
官网参考:HttpRequest.Files
- 也可以利用Ajax提交,FormData(),上传的文件支持移除,
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="InputFileAjax.aspx.cs" Inherits="WebFormTest.FileUpload.InputFileAjax" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <style> /*为了不显示控件的 未选择任何文件/上传个数*/ .surfaceThree { position: relative; width: 80px; background: gainsboro; border-radius: 6px; text-align: center; line-height: 30px; font-size: 16px; color: black; } .surfaceThree input { position: absolute; top: 5px; right: -110px; height: 30px; /* 重点代码让input隐藏 */ opacity: 0; } </style> <script src="../Scripts/jquery-1.10.2.min.js"></script> </head> <body> <form id="form1" runat="server"> <div id="email" runat="server"> </div> <div class="surfaceThree"> 选择文件 <input type="file" multiple="multiple" name="fileUpload" id="fileUpload" onchange="uploadLogic(this)" /> </div> <br /> <br /> <input type="text" name="author" value="huy" /> <asp:Button ID="Button1" runat="server" Text="上传到服务器" OnClick="Button1_Click" OnClientClick="return addFiles()" /> <asp:Label ID="Label1" runat="server" Text="" Style="color: Red"></asp:Label> </form> <script type="text/javascript"> //单击上传的时候 增加手动选择的附件 function addFiles() { var formData = new FormData(); for (var i = 0; i < fileLists.length; i++) { formData.append('file', fileLists[i]); //file可以任意 } $.ajax({ url: "InputFileAjax.ashx", data: formData, type: "post", async: false, contentType: false, processData: false, success: function (msg) { var rs = jQuery.parseJSON(data); if (rs.ReturnCode == 1) { console.log("手動添加附件成功"); } else if (rs.ReturnCode == 0) { console.log(rs.Message); } else { alert("手動添加附件失敗:" + rs.Message); } }, error: function (e) { alert("手動添加附件失敗"); } }); } fileLists = []; //多文件上传逻辑 function uploadLogic(obj) { var filrarr = obj.files; //console.log("这次上传的文件:"); //console.log(filrarr); if ((fileLists.length + filrarr.length) > 10) { alert("手動添加附件不鞥超過10個!"); return; } files = Array.prototype.slice.call(filrarr); //需要能动态修改fileList即可,第一想法是将它转化为数组进行操作。 fileLists = fileLists.concat(files); console.log("现在总的文件:"); console.log(fileLists); var email = $('#email'); for (var i = 0; i < filrarr.length; i++) { var addtr = $("<div style='margin: 5px 10px;display:inline-block'>" + "<div class='label label-primary' title='" + filrarr[i].name + "' style='vertical-align: middle;margin-right: 10px;display:inline-block;width:150px;overflow: hidden; text-overflow: ellipsis;height:20px'>" + filrarr[i].name + "</div>" + "<input type=\"button\" id='btnRemoveAttach' style='color: red;border-radius: 6px' onclick='RemoveMulFileUpload(this,\"" + filrarr[i].name + "\")' value=\"移除\" />" + "</div>"); email.append(addtr); } } //移除多文件上传控件的文件 function RemoveMulFileUpload(div, filename) { var ind = $(div).parent().index(); fileLists.splice(ind, 1);//修改fileLists $(div).parent().remove(); console.log("删除文件:" + filename + "。后现在总的文件:"); console.log(fileLists); } </script> </body> </html>
注意:
1、processData参数:
用于对data参数进行序列化处理,默认值是true。默认情况下发送的数据将被转换为对象,对于文件File则不需要转换,此参数需要设置为false。否则可能报非法调用(Illegal invocation)。
2、普通参数传递与获取:
formData.append("param1","参数1");
string p1=Request.Form["param1"]; //普通参数获取
后端 一般处理页
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; namespace WebFormTest.FileUpload { /// <summary> /// InputFileAjax1 的摘要说明 /// </summary> public class InputFileAjax1 : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; //context.Response.Write("Hello World"); AddUploadFiles(context); } /// <summary> /// 增加手动上传的文件 /// </summary> /// <param name="context"></param> private void AddUploadFiles(HttpContext context) { var result = new Result() { ReturnCode = 0 }; try { if (context.Request.Files.Count <= 0) { result.Message = "沒有額外添加附件"; context.Response.Write(JsonConvert.SerializeObject(result)); } if (context.Request.Files.Count > 10) { result.Message = "手動添加附件不鞥超過10個"; context.Response.Write(JsonConvert.SerializeObject(result)); } HttpFileCollection files = context.Request.Files; if (files.Count > 0) { for (int i = 0; i < files.Count; i++) { files[i].SaveAs(context.Server.MapPath("~/Files/") + Path.GetFileName(files[i].FileName)); } } context.Response.Write(JsonConvert.SerializeObject(result)); } catch (Exception ex) { result.Message = ex.ToString(); context.Response.Write(JsonConvert.SerializeObject(result)); } } public bool IsReusable { get { return false; } } } public class Result { /// <summary> /// 1成功,0错误 /// </summary> public int ReturnCode { get; set; } public string Message { get; set; } } }
其中:未选择任何文件/上传个数不显示
常规的是这样,
选择文件后,这个“未选择任何文件”则显示成 选择了几个文件。但是移除文件 这个个数不会变。所以想办法不需要显示这个。
.surfaceThree { position: relative; /*height: 30px;*/ width: 80px; background: gainsboro; border-radius: 6px; text-align: center; line-height: 30px; font-size:16px; color: black; } .surfaceThree input { position: absolute; top: 5px; right: -110px; /*color: #fff;*/ height: 30px; /* 重点代码让input隐藏 */ opacity: 0; } <div class="surfaceThree">选择文件 <input type="file" multiple="multiple" name="fileUpload" id="fileUpload" onchange="uploadLogic(this)" /> </div>
上传图片限制大小、类型、像素
用到input标签来上传图片。图片有很多格式我们只需要其中的几种,就需要对用户上传的文件进行验证,在HTML5中有一个新的属性:accept文件类型限制。但是通常我们会用javascript或jQuery编写方法进行验证图片的大小限制、类型判断、像素判断。
function handleChange(file) { var fileTypes = [".jpg", ".png"]; //我们所需要的图片格式 var filePath = file.value; if (filePath) { var filePic = file.files[0]; //选择的文件内容--图片 var fileType = filePath.slice(filePath.indexOf(".")); //选择文件的格式 var fileSize = file.files[0].size; //选择文件的大 if (fileTypes.indexOf(fileType) == -1) { //判断文件格式是否符合要求 alert("文件格式不符合要求!"); return if (fileSize > 1024 * 1024) { alert("文件大小不能超过1M!"); return var reader = new FileReader(); reader.readAsDataURL(filePic); reader.onload = function (e) { var data = e.target.result; //加载图片获取图片真实宽度和高度 var image = new Image(); image.onload = function () { var width = image.width; var height = image.height; if (width == 720 | height == 1280) { //判断文件像素 //上传图片 } else { alert("图片尺寸应为:720*1280!"); return; } }; image.src = data; }; } else { return } }
表单提交
前端控件都应该有一个name属性和”当前值“,在提交时,它们将以 name=value 的形式做为提交数据的一部分。
前台form表单的提交方式有很多种,例如:
1、form表单submit直接提交的方法
2、Ajax提交的方法
j3、query提交的方法
这里总结一下ajax提交的两种方式:
1.serialize() 方法:
通过序列化表单值,创建 URL 编码文本字符串。我们可以选择一个或多个表单元素(比如 input 及/或 文本框),或者 form 元素本身。序列化的值可在生成 AJAX 请求时用于 URL 查询字符串中。
所用到的语法为:$("form").serialize()
提交方法的代码段:$('form').submit(function() {
alert($(this).serialize());
return false;
});
最终序列化后,表单中数据会以下面这种方式提交到后台:a=1&b=2&c=3&d=4&e=5
这种方式处理表单时所适用的input标签类型是有限的,只适用于一些常用的类型例如text、checkbox、select、date等等,但是对于file文件类型的input框并不适用,那我们在用到ajax提交方式的时候应该如何提交file类型的input框数据呢?
2.封装FormData 对象,直接用$.ajax提交
将form表单中的内容封装成formdata的数据格式
FormData 对象可以把form中所有表单元素的name与value组成一个queryString,提交到后台,在使用Ajax提交时,使用FormData对象可以减少拼接queryString的工作量。
FormData的使用方法也是非常简单,直接传入form表单对象即可,如下:
var form = $('#form1');
var formdata = new FormData(form);
使用这种方式将数据封装后,file类型的文件数据即可以键值对的方式封装在formdata中,然后用ajx提交,方法如下:
$.ajax({ type : "POST", url : "houtai/123.do", data : formData, async: false, cache: false, contentType: false, processData: false, success : function(msg) { if(msg){ alert('提交成功!'); } } });
需要注意的是:以formdata的方式提交时需要添加async: false, 同步,否则后台无法接收到前台传过来的file文件数据,这样的提交方式,既可以提交任何一种type类型标签,又可以在提交之后得到返回结果,方便快捷又实用。
二、IIS搭建HTTP文件服务器(文件上传下载)
IIS环境准备
1、 安装IIS
打开“控制面板”,找到“程序与功能”, 找到“启用或关闭Windows功能”,点进去之后,将“Internet Information Services”下所有节点都打勾(这样就搭建了一个功能完全的HTTP/FTP服务器),注意“WebDAV发布”必须要安装,这个跟文件服务器中文件访问权限有着很大的关系,如果想对服务器中某个具有读写权限的文件夹进行读写,就必须开启该选项,如下图所示:
2、 添加网站
添加自己的一个网站,鼠标移到“网站”上方,右键点击鼠标,弹出菜单,在菜单中点击“添加网站”,如下图所示:
根据如下图所说的步骤,填写网站名称及选择物理路径,其他默认即可,然后点击“确定”按钮:
本网站仅作为文件服务器,因此,将服务器的文件浏览功能打开,以便浏览,具体操作为鼠标双击“目录浏览”后,将“操作”一栏里的“启用”打开,如下图所示:
3、WebDAV创作规则
进入,点击“WebDAV设置”,将①②所示红色框内的属性设置为图中所示的属性,并点击“应用”,如下图所示:
返回到“WebDAV创作规则”,点击“添加创作规则”,如下图所示:
在弹出的“添加创作规则”,将“允许访问此内容”选中,权限“读取、源、写入”都打勾,点击“确定”按钮关闭,如下图所示:
返回到“WebDAV创作规则”,点击“启用WebDAV”,
双击“身份验证”,将“匿名身份验证”(客户端读取文件)及“Windows身份验证”(客户端写入、删除)启用,如下所示:
为了能让文件服务器具有写入、删除功能,可以在现有Windows系统账户上新建一个隶属于“Power Users”的账户“test”(密码:123),如下图所示:
以上关于如何创建账户的内容,请自行百度
为了能让test账户顺利访问存放于E盘下的“TestWebSite”文件夹,需要为该文件夹设置Power Users组的访问权限,如下图所示:
关于如何将特定组或用户设置权限的问题,请自行百度。
查看本机IIS的IP地址,并在浏览器输入该IP,将会显示网站下内容。
自此,IIS文件服务器的搭建已经完毕。
使用C# WebClient访问IIS文件服务器
在使用WebClient类之前,必须先引用System.Net命名空间,文件下载、上传与删除的都是使用异步编程,也可以使用同步编程,
这里以异步编程为例:
1) 文件下载:
//下载文件 string fileHttpPath = @"http://localhost/js/javascript/01.js"; string saveName = @"E:\IIS_Deploy\FileSystem\01.js"; SY.Filer.Http.HttpDownloadFile.DownloadFileAsync(fileHttpPath,saveName); Console.WriteLine("文件下载结束"); /// <summary> /// http文件下载 /// </summary> public class HttpDownloadFile { /// <summary> /// 异步文件下载 /// </summary> /// <param name="fileHttpPath">文件的http路径</param> /// <param name="saveLocalName">本地保存的文件名称</param> public static void DownloadFileAsync(string fileHttpPath, string saveLocalName) { try { using (WebClient client = new WebClient()) { //使用默认的凭据——读取的时候,只需默认凭据就可以 client.Credentials = CredentialCache.DefaultCredentials; Uri uri = new Uri(fileHttpPath); client.DownloadProgressChanged += Client_DownloadProgressChanged; client.DownloadFileCompleted += Client_DownloadFileCompleted; //异步下载指定路径 client.DownloadFileAsync(uri, saveLocalName); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } //下载完成事件处理程序 private static void Client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { if (e.Cancelled) { Console.WriteLine("文件下载被取消"); } //获取一个值,该值指示异步操作期间发生的错误 if (e.Error == null) { Console.WriteLine("文件下载成功"); } else { //throw e.Error; //【异步操作 上层捕获不到】 Console.WriteLine(e.Error.ToString()); } } //下载进度事件处理程序 private static void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { Console.WriteLine($"{e.ProgressPercentage}:{e.BytesReceived}/{e.TotalBytesToReceive}"); } }
2) 文件上传:
//下载上传 string fileHttpPath = @"http://localhost/txt/Readme.txt"; string localfile = @"D:\0、入职20180820\Readme.txt"; SY.Filer.Http.HttpUploadFile.UploadFileAsync(fileHttpPath, localfile); Console.WriteLine("文件上传结束"); /// <summary> /// http文件上传 /// </summary> public class HttpUploadFile { /// <summary> /// 异步文件上传 /// </summary> /// <param name="fileSaveHttpPath">文件的http路径</param> /// <param name="saveLocalName">本地的文件名称</param> public static void UploadFileAsync(string fileSaveHttpPath, string localFile) { try { using (WebClient client = new WebClient()) { //client.Credentials = CredentialCache.DefaultCredentials; //使用Windows登录方式 client.Credentials = new NetworkCredential("test", "hy123456@"); Uri _uri = new Uri(fileSaveHttpPath); //注册上传进度事件通知 client.UploadProgressChanged += Client_UploadProgressChanged; client.UploadFileCompleted += Client_UploadFileCompleted; //异步 将选择的上传文件到服务器 client.UploadFileAsync(_uri, "PUT", localFile); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } //上传完成事件处理程序 private static void Client_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e) { if (e.Cancelled) { Console.WriteLine("文件上传被取消"); return; } //获取一个值,该值指示异步操作期间发生的错误 if (e.Error == null) { Console.WriteLine("文件上传成功"); } else { //throw e.Error; //【异步操作 上层捕获不到】 Console.WriteLine(e.Error.ToString()); } } //上传进度事件处理程序 private static void Client_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e) { Console.WriteLine($"{e.ProgressPercentage}:{e.BytesSent}/{e.TotalBytesToSend}"); } }
三、异常及解决方案
1、上传文件报错:远程服务器返回错误: (409) 冲突
由于服务器上不存在上传文件设定的目录,而webclient不会自动创建文件夹所导致。手工创建对应的文件夹即可。
2、上传文件报错:远程服务器返回错误: (403) 已禁止
服务器上文件夹未设置用户组某用户的访问权限