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 = "上传失败";
            }
        }
View Code

允许一次选择多个文件: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 = "上传成功!";
         }
     }
View Code

选择多个文件: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 = "上传失败";
            }
        }
View Code

注意: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>
View Code

注意:

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; }
    }
}
View Code

其中:未选择任何文件/上传个数不显示

常规的是这样,

选择文件后,这个“未选择任何文件”则显示成 选择了几个文件。但是移除文件 这个个数不会变。所以想办法不需要显示这个。

      .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>
View Code

 

上传图片限制大小、类型、像素

用到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
    }
}
View Code

 

表单提交

前端控件都应该有一个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('提交成功!');
            } 
        }  
});  
View Code

需要注意的是:以formdata的方式提交时需要添加async: false, 同步,否则后台无法接收到前台传过来的file文件数据,这样的提交方式,既可以提交任何一种type类型标签,又可以在提交之后得到返回结果,方便快捷又实用。

form表单提交过程

 

二、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}");
        }
} 
View Code

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}");
        }


    }
View Code

 

三、异常及解决方案

1、上传文件报错:远程服务器返回错误: (409) 冲突

  由于服务器上不存在上传文件设定的目录,而webclient不会自动创建文件夹所导致。手工创建对应的文件夹即可。

2、上传文件报错:远程服务器返回错误: (403) 已禁止

   服务器上文件夹未设置用户组某用户的访问权限

 

 

posted @ 2017-08-10 18:34  peterYong  阅读(1178)  评论(0编辑  收藏  举报