轉:MVC中的文件上傳

 

上傳文件與與上傳數據區別javascript

上傳數據主要指json等簡單字符串,上傳文件指的是上傳word、excel圖片等。在上傳數據的時候enctype默認爲第一個application/x-www-form-urlencoded,而上傳數據包含文件的時候要用第二種multipart/form-datahtml

 

描述
application/x-www-form-urlencoded 在發送前編碼全部字符(默認)url編碼
multipart/form-data

不對字符編碼。前端

在使用包含文件上傳控件的表單時,必須使用該值。java

text/plain 空格轉換爲 "+" 加號,但不對特殊字符編碼。

 

 

 

 

文件上傳在WEB開發中應用很普遍,咱們常常發微博、發微信朋友圈都用到了圖片上傳功能。jquery

文件上傳是指將本地圖片、視頻、音頻等文件上傳到服務器上,能夠供其餘用戶瀏覽或下載的過程。web

今天我給你們聊聊常見的文件(圖片)上傳的方式和要點處理。ajax

表單上傳json

這是傳統的form表單上傳,使用form表單的input[type=」file」]控件,能夠打開系統的文件選擇對話框,從而達到選擇文件並上傳的目的,它的好處是多瀏覽器兼容,它是web開發者最經常使用的一種文件上傳方式。api

表單的代碼以下:跨域

<formmethod="post"action="http://uploadUrl"enctype="multipart/form-data">

   <inputname="file"type="file"accept="image/gif,image.jpg"/>

   <inputname="token"type="hidden"/>

   <inputtype="submit"value="提交"/>

</form>

如下是表單上傳幾個關鍵點:

  • method=」post」: 採用post方式提交數據
  • enctype=」multipart/form- data」:採用multipart格式上傳文件,此時request頭會顯示 Content-Type:multipart/form-data; boundary=—-WebKitFormBoundaryzr34cwJ67R95KQC9
  • action:標明上傳的服務端處理地址
  • type=」file」:使用input的file控件上傳
  • 若是是多文件批量上傳,能夠將input[type=」file」]的name屬性設置爲如:name=」file[]」
  • accept屬性是HTML5的新屬性,它規定了可經過文件上傳提交的文件類型
  • 上傳的觸發事件能夠是:input[type=」file」]的onChange觸發,也能夠由一個獨立的按鈕的onClick使整個表單提交,此時還能夠用input[type=」hidden」]帶一些其它的參數,好比Token來源驗證等等。

Ajax無刷新上傳

Ajax無刷新上傳的方式,本質上與表單上傳無異,只是把表單裏的內容提出來採用ajax提交,而且由前端決定請求結果回傳後的展現結果,不用像直接表單上傳那樣刷新和跳轉頁面。在這裏,咱們採用jQuery來做爲操做DOM和建立ajax提交的js基礎庫。

html代碼片斷以下:

<form>

   <inputid="file"name="file"type="file"/>

   <inputid="token"name="token"type="hidden"/>

</form>

JavaScript代碼片斷以下:

$("#file").on("change",function(){

 var formData =newFormData();

  formData.append("file", $("#file")[0].files);

  formData.append("token", $("#token").val());

  $.ajax({

      url:"http://uploadUrl",

      type:"POST",

      data: formData,

      processData:false,

      contentType:false,

      success:function(response){

             //根據返回結果指定界面操做

      }

  });

});

咱們使用了file控件的change來觸發上傳事件,固然你也可使用某個按鈕來觸發表單提交。提交數據時,我 用到了FormData對象來發送二進制文件,FormData構造函數提供的append()方法,除了直接添加二進制文件還能夠附帶一些其它的參數, 做爲XMLHttpRequest實例的參數提交給服務端。

使用jquery提供的ajax方法來發送二進制文件,還須要附加兩個參數:

  • processData: false // 不要對data參數進行序列化處理,默認爲true
  • contentType: false // 不要設置Content-Type請求頭,由於文件數據是以 multipart/form-data 來編碼

表單上傳和ajax上傳實質是同樣的,只不過表單上傳已經封裝好了,比較淺顯,ajax比較容易控制

 

上傳與安全

上傳文件時必須作好文件的安全性,除了前端必要的驗證,如文件類型、後綴、大小等驗證,重要的仍是要在後臺作安全策略。

這裏我列舉幾個注意點:

    • 後臺須要進行文件類型、大小、來源等驗證
    • 定義一個.htaccess文件,只容許訪問指定擴展名的文件。
    • 將上傳後的文件生成一個隨機的文件名,而且加上此前生成的文件擴展名。
    • 設置上傳目錄執行權限,避免不懷好意的人繞過如圖片擴展名進行惡意攻擊,拒絕腳本執行的可能性。

 

轉:http://blog.csdn.net/xcymorningsun/article/details/52949848

 

http://blog.sina.com.cn/s/blog_75a555e40101q8i7.html

 

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-2

WEBAPI獲取數據文件等

/// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<HttpResponseMessage> PostFormData()
        {
            // 檢查該請求是否含有multipart/form-data 
            if (!Request.Content.IsMimeMultipartContent("form-data"))
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
            // 文件保存目錄路徑
            const string saveTempPath = "~/UploadFiles/";
            var dirTempPath = HttpContext.Current.Server.MapPath(saveTempPath);
            // 設置上傳目錄 
            var provider = new MultipartFormDataStreamProvider(dirTempPath);

            try
            {
                // 讀取表單數據 
                await Request.Content.ReadAsMultipartAsync(provider);
                // 顯示全部「鍵-值」對  
                foreach (var key in provider.FormData.AllKeys)
                {
                    foreach (var val in provider.FormData.GetValues(key))
                    {
                        System.Diagnostics.Trace.WriteLine(string.Format("{0}: {1}", key, val));
                    }
                }

                // 如下描述如何獲取文件名    
                //TODO:這樣作直接就將文件存到了指定目錄下,暫時不知道如何實現只接收文件數據流但並不保存至服務器的目錄下,由開發自行指定如何存儲,好比經過服務存到圖片服務器 
                foreach (MultipartFileData file in provider.FileData)
                {
                    System.Diagnostics.Trace.WriteLine(file.Headers.ContentDisposition.FileName);//獲取上傳文件實際的文件名
                    System.Diagnostics.Trace.WriteLine("Server file path: " + file.LocalFileName);//獲取上傳文件在服務上默認的文件名
                }
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
            }
        }
WEBAPI

 ajax

受瀏覽器的同源策略限制,JavaSript只能請求本域內的資源。跨域資源共享(Cross-Origin Resource Sharing, CORS)是爲解決Ajax技術難實現跨域問題而提出的一個規範,這個規範試着從根本上解決安全的跨域資源共享問題。在此以前,解決此類問題的途徑每每是服務器代理、JSONP等,治標不治本。目前基本全部瀏覽器都已經支持該規範。
一個域是由schema、host、port三者共同組成,與路徑無關。所謂跨域,是指在http://example-foo.com/域上經過XMLHttpRequest對象調用http://example-bar.com/域上的資源。CORS約定服務器端和瀏覽器在HTTP協議之上,經過一些額外HTTP頭部信息,進行跨域資源共享的協商。服務器端和瀏覽器都必需遵循規範中的要求。
CORS把HTTP請求分紅兩類,不一樣類別按不一樣的策略進行跨域資源共享協商。
1. 簡單跨域請求。
當HTTP請求出現如下兩種狀況時,瀏覽器認爲是簡單跨域請求:
1). 請求方法是GET、HEAD或者POST,而且當請求方法是POST時,Content-Type必須是application/x-www-form-urlencoded, multipart/form-data或着text/plain中的一個值。
2). 請求中沒有自定義HTTP頭部。
對於簡單跨域請求,瀏覽器要作的就是在HTTP請求中添加Origin Header,將JavaScript腳本所在域填充進去,向其餘域的服務器請求資源。服務器端收到一個簡單跨域請求後,根據資源權限配置,在響應頭中添加Access-Control-Allow-Origin Header。瀏覽器收到響應後,查看Access-Control-Allow-Origin Header,若是當前域已經獲得受權,則將結果返回給JavaScript。不然瀏覽器忽略這次響應。
2. 帶預檢(Preflighted)的跨域請求。
當HTTP請求出現如下兩種狀況時,瀏覽器認爲是帶預檢(Preflighted)的跨域請求:
1). 除GET、HEAD和POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)之外的其餘HTTP方法。
2). 請求中出現自定義HTTP頭部。
帶預檢(Preflighted)的跨域請求須要瀏覽器在發送真實HTTP請求以前先發送一個OPTIONS的預檢請求,檢測服務器端是否支持真實請求進行跨域資源訪問,真實請求的信息在OPTIONS請求中經過Access-Control-Request-Method Header和Access-Control-Request-Headers Header描述,此外與簡單跨域請求同樣,瀏覽器也會添加Origin Header。服務器端接到預檢請求後,根據資源權限配置,在響應頭中放入Access-Control-Allow-Origin Header、Access-Control-Allow-Methods和Access-Control-Allow-Headers Header,分別表示容許跨域資源請求的域、請求方法和請求頭。此外,服務器端還能夠加入Access-Control-Max-Age Header,容許瀏覽器在指定時間內,無需再發送預檢請求進行協商,直接用本次協商結果便可。瀏覽器根據OPTIONS請求返回的結果來決定是否繼續發送真實的請求進行跨域資源訪問。這個過程對真實請求的調用者來講是透明的。
XMLHttpRequest支持經過withCredentials屬性實如今跨域請求攜帶身份信息(Credential,例如Cookie或者HTTP認證信息)。瀏覽器將攜帶Cookie Header的請求發送到服務器端後,若是服務器沒有響應Access-Control-Allow-Credentials Header,那麼瀏覽器會忽略掉此次響應。
這裏討論的HTTP請求是指由Ajax XMLHttpRequest對象發起的,全部的CORS HTTP請求頭均可由瀏覽器填充,無需在XMLHttpRequest對象中設置。如下是CORS協議規定的HTTP頭,用來進行瀏覽器發起跨域資源請求時進行協商:
1. Origin。HTTP請求頭,任何涉及CORS的請求都必需攜帶。
2. Access-Control-Request-Method。HTTP請求頭,在帶預檢(Preflighted)的跨域請求中用來表示真實請求的方法。
3. Access-Control-Request-Headers。HTTP請求頭,在帶預檢(Preflighted)的跨域請求中用來表示真實請求的自定義Header列表。
4. Access-Control-Allow-Origin。HTTP響應頭,指定服務器端容許進行跨域資源訪問的來源域。能夠用通配符*表示容許任何域的javascript訪問資源,可是在響應一個攜帶身份信息(Credential)的HTTP請求時,Access-Control-Allow-Origin必需指定具體的域,不能用通配符。
5. Access-Control-Allow-Methods。HTTP響應頭,指定服務器容許進行跨域資源訪問的請求方法列表,通常用在響應預檢請求上。
6. Access-Control-Allow-Headers。HTTP響應頭,指定服務器容許進行跨域資源訪問的請求頭列表,通常用在響應預檢請求上。
7. Access-Control-Max-Age。HTTP響應頭,用在響應預檢請求上,表示本次預檢響應的有效時間。在此時間內,瀏覽器均可以根據這次協商結果決定是否有必要直接發送真實請求,而無需再次發送預檢請求。
8. Access-Control-Allow-Credentials。HTTP響應頭,凡是瀏覽器請求中攜帶了身份信息,而響應頭中沒有返回Access-Control-Allow-Credentials: true的,瀏覽器都會忽略這次響應。

總結:只要是帶自定義header的跨域請求,在發送真實請求前都會先發送OPTIONS請求,瀏覽器根據OPTIONS請求返回的結果來決定是否繼續發送真實的請求進行跨域資源訪問。因此複雜請求確定會兩次請求服務端。
View Code

 

response.setHeader("Access-Control-Allow-Origin", "*"); 
「Access-Control-Allow-Origin」表示容許跨域訪問,「*」表示容許全部來源進行跨域訪問,這裏也能夠替換爲特定的域名或ip。 
很顯然,這種方式對非網站擁有人員來講是不能作到的。並且此種方式很容易受到CSRF攻擊

 

beforeSend: function (request) {
request.AddHeader("Access-Control-Allow-Origin", "*");

request.setRequestHeader("Test", "ceshi01");

}

action:

//服務端容許跨域訪問  

 HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");  

 HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT");  

 HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "*");  

 

JS 上傳文件和參數

 

 xhr.setRequestHeader("POWERED-BY-MENGXIANHUI", "Approve");  

 xhr.setRequestHeader("Content-Type", "application/xml");  

xhr.onreadystatechange = handler;  

 /*發送文件 文件以數組的方式傳入 支持多個*/
    importFiles: function (options) {
        var host = webInit.url;
        var xhr = new XMLHttpRequest();
        var iUrl = host + options.url;

        if (true) {
            xhr.open("POST", iUrl, true);
            layerIndexIm = layer.load(2);
            var formData = new FormData();
            formData.append("time", (new Date()).valueOf());
            formData.append("uid", window.sessionStorage.getItem("uid"));
            formData.append("data", JSON.stringify(options.data));

            for (var i = 0, len = options.file.length; i < len; i++) {
                formData.append("file" + i, options.file[i]);
            }

            xhr.onload = function (result) {
                if (result.currentTarget.status == 200) {
                    var resultData = JSON.parse(result.currentTarget.response);
                    if (options.success) {
                        options.success.call(this,resultData);
                    }
                    layer.close(layerIndexIm);
                    if (resultData.code == 0) {
                        // layer.msg( "導入成功。");
                    } else if (resultData.code == -1) {
                        var mes = resultData.msg || "請求失敗,請聯繫管理員。";
                        layer.msg(mes);
                    } else {
                        layer.msg(resultData.msg);
                    }
                } else {
                    layer.close(layerIndexIm);
                    layer.msg("請求失敗,請檢查文件後從新導入。");
                }
            };
            xhr.send(formData);
        }
    },
    /*獲取連接中的關鍵字的值*/
    getUrlParam: function (name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return decodeURI(r[2]);
        return null;
    },
    formatDateTime: function (date) {
        if (typeof (date) == 'object') {
            var y = date.getFullYear();
            var m = date.getMonth() + 1;
            m = m < 10 ? '0' + m : m;
            var d = date.getDate();
            d = d < 10 ? ('0' + d) : d;
            return y + '-' + m + '-' + d;
        } else {
            return date;
        }

    }
View Code

http://blog.csdn.net/net_lover/article/details/5172522

相關文章
相關標籤/搜索