1. 程式人生 > >javascript+C#本地大檔案上傳到伺服器方法(WebUploader)

javascript+C#本地大檔案上傳到伺服器方法(WebUploader)

  在前後端進行資料傳輸時,有時需要傳輸大檔案。WebUploader提供了一些前端傳輸圖片和檔案等地方法。但是,當上傳檔案較大時,會被伺服器端限制,阻止其上傳。
  在ASP.Net中,調整伺服器接受檔案的大小的配置方法如下:

<httpRuntime executionTimeout="90" maxRequestLength="40960" useFullyQualifiedRedirectUrl="false"minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100" enableVersionHeader="false"/>  

  每個引數的作用為:
  executionTimeout:表示允許執行請求的最大時間限制,單位為秒 。
  maxRequestLength:指示 ASP.NET支援的最大檔案上載大小。該限制可用於防止因使用者將大量檔案傳遞到該伺服器而導致的拒絕服務攻擊。指定的大小以 KB 為單位。預設值為 4096KB (4 MB)。
  useFullyQualifiedRedirectUrl:表示指示客戶端重定向是否是完全限定的(採用”http://server/path” 格式,這是某些移動控制元件所必需的),或者指示是否代之以將相對重定向傳送到客戶端。如果為True,則所有不是完全限定的重定向都將自動轉換為完全限定的格式。false 是預設選項。
  minFreeThreads

:表示指定允許執行新請求的自由執行緒的最小數目。ASP.NET為要求附加執行緒來完成其處理的請求而使指定數目的執行緒保持自由狀態。預設值為 8。
  minLocalRequestFreeThreads:表示ASP.NET保持的允許執行新本地請求的自由執行緒的最小數目。該執行緒數目是為從本地主機傳入的請求而保留的,以防某些請求在其處理期間發出對本地主機的子請求。這避免了可能的因遞迴重新進入Web 伺服器而導致的死鎖。
  appRequestQueueLimit:表示ASP.NET將為應用程式排隊的請求的最大數目。當沒有足夠的自由執行緒來處理請求時,將對請求進行排隊。當佇列超出了該設定中指定的限制時,將通過“503 -伺服器太忙”錯誤資訊拒絕傳入的請求。 enableVersionHeader:表示指定 ASP.NET是否應輸出版本標頭。Microsoft Visual Studio 2005 使用該屬性來確定當前使用的 ASP.NET版本。對於生產環境,該屬性不是必需的,可以禁用。

    
  但是,當檔案過大時會即使Web允許通過,IIS也會拒絕傳輸,這時,就需要使用分片上傳。
  分片上傳是指將想要上傳的檔案在前端切割成很小的一片,依次傳給伺服器,再在伺服器端將檔案組合成完整的檔案。 javascript自身提供了上傳檔案的方法也提供了上傳檔案的一種物件方法FormData。可以看JS+WebService 大檔案分片上傳程式碼及解析這篇文章地JS部分。
  Web Uploader封裝好了上傳的方法,可以直接使用
html部分

<div id="uploaderrecovery" class="wu-example">
     <div id="thelistrecovery" class="uploader-list"></div>
     <div class="btns">
         <div id="pickerrecovery">選擇檔案</div>
         <button id="ctlBtnrecovery" class="btn btn-default">開始上傳</button>
     </div>
 </div>

還要記得引用Web Uploader檔案

javascript部分

if (recoveryindex === 0) {
    Recovery();
}
var recoveryindex = 0;  //全域性變數,判斷uploader例項化次數,注意只能例項化一次
function Recovery() {
    recoveryindex = 1;
    var GUID  = WebUploader.Base.guid(); //當前頁面是生成的GUID作為標示
    var $list = $("#thelistrecovery");
    var uploader = WebUploader.create({

        // swf檔案路徑
        swf: 'Libs/webuploader/0.1.5/Uploader.swf',

        // 檔案接收服務端。
        server: WebBuckupAddress, //C#地址:http://localhost:7792/Backup.asmx/backuprecovery

        // 選擇檔案的按鈕。可選。
        // 內部根據當前執行是建立,可能是input元素,也可能是flash.
        pick: '#pickerrecovery',
        formData: { guid: GUID },
        // 不壓縮image, 預設如果是jpeg,檔案上傳前會壓縮一把再上傳!
        resize: false,
        chunked:true,
        threads: 1,
        accept: { //指定格式為zip
            title: 'File',
            extensions: 'zip',
            mimeTypes: 'application/zip'}
    })
    // 當有檔案被新增進佇列的時候
    uploader.on( 'fileQueued', function( file ) {
        $list.append('<div id="' + file.id + '" class="item">' +
            '<h4 id="recoveryinfo" class="info">' + file.name + '</h4>' +
            '<p class="state">等待上傳...</p>' +
            '</div>');

    });
    uploader.on( 'beforeFileQueued', function( file ) {
        $list.empty();
        uploader.reset();
        uploader.reset();
    });
    // 檔案上傳過程中建立進度條實時顯示。
    uploader.on( 'uploadProgress', function( file, percentage ) {
        var $li = $( '#'+file.id ),
            $percent = $li.find('.progress .progress-bar');

        // 避免重複建立
        if ( !$percent.length ) {
            $percent = $('<div class="progress progress-striped active">' +
                '<div class="progress-bar" role="progressbar" style="width: 0%">' +
                '</div>' +
                '</div>').appendTo( $li ).find('.progress-bar');
        }

        $li.find('p.state').text('上傳中');

        $percent.css( 'width', percentage * 100 + '%' );
    });
    uploader.on( 'uploadSuccess', function( file ) {
        $( '#'+file.id ).find('p.state').text('已上傳');
    });

    uploader.on( 'uploadError', function( file ,code) {
        $( '#'+file.id ).find('p.state').text('上傳出錯');
        alert(code);
    });

    uploader.on( 'uploadComplete', function( file ) {
        $( '#'+file.id ).find('.progress').fadeOut();
    });

    uploader.on( 'uploadAccept', function( file, response ) {
        if ( response['hasError']!=false ) {
            // 通過return false來告訴元件,此檔案上傳有錯。

            return false;
        }
        else
        {
            return true;
        }
    });
    $("#ctlBtnrecovery").on('click', function () {
        uploader.upload();
    });
}

C#部分

Web.config檔案

<configuration>
    <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
    </system.web>

    <!--這部分為新增的配置跨域-->
<system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Methods" value="OPTIONS,POST,GET"/>
        <add name="Access-Control-Allow-Headers" value="x-requested-with,content-type"/>
        <add name="Access-Control-Allow-Origin" value="*"/>
      </customHeaders>
    </httpProtocol>

</configuration>

WebService.asmx檔案

[WebMethod]
public void backuprecovery()
{
    BackupRecovery br = new BackupRecovery();
    br.Recovery();
}
public void Recovery()
{
    HttpContext context = System.Web.HttpContext.Current;
    context.Response.ContentType = "text/plain";
    context.Response.Write("{\"chunked\" : true, \"hasError\" : false}");
    //如果進行了分片
    if (context.Request.Form.AllKeys.Any(m => m == "chunk"))
    {
        //取得chunk和chunks
        int chunk = Convert.ToInt32(context.Request.Form["chunk"]);//當前分片在上傳分片中的順序(從0開始)
        int chunks = Convert.ToInt32(context.Request.Form["chunks"]);//總分片數
        //根據GUID建立用該GUID命名的臨時資料夾
        //string folder = context.Server.MapPath("~/1/" + context.Request["guid"] + "/");
        string folder = backuprecovery + context.Request["guid"] + "/"; //檔案存放絕對路徑由寫好地backuprecovery 和前端設定好的guid決定
        string path = folder + chunk;

        //建立臨時傳輸資料夾
        if (!Directory.Exists(Path.GetDirectoryName(folder)))
        {
            Directory.CreateDirectory(folder);
        }
        FileStream addFile = new FileStream(path, FileMode.Append, FileAccess.Write);
        BinaryWriter AddWriter = new BinaryWriter(addFile);
        //獲得上傳的分片資料流
        HttpPostedFile file = context.Request.Files[0];
        Stream stream = file.InputStream;

        BinaryReader TempReader = new BinaryReader(stream);
        //將上傳的分片追加到臨時檔案末尾
        AddWriter.Write(TempReader.ReadBytes((int)stream.Length));
        //關閉BinaryReader檔案閱讀器
        TempReader.Close();
        stream.Close();
        AddWriter.Close();
        addFile.Close();

        TempReader.Dispose();
        stream.Dispose();
        AddWriter.Dispose();
        addFile.Dispose();
        if (chunk == chunks - 1)
        {
            ProcessRequest(context.Request["guid"], Path.GetExtension(file.FileName));
        }
    }
    else//沒有分片直接儲存
    {
        DeleteFolder(backuprecoveryfile);
        string targetPath = backuprecovery + "data" + Path.GetExtension(context.Request.Files[0].FileName);  //data為指定zip的名字
        context.Request.Files[0].SaveAs(targetPath);
    }
}
private void ProcessRequest(string guid, string fileExt)
{
    HttpContext context = System.Web.HttpContext.Current;
    context.Response.ContentType = "text/plain";
    string sourcePath = Path.Combine(backuprecovery, guid + "/");//源資料資料夾
    string targetPath = Path.Combine(backuprecovery, Guid.NewGuid() + fileExt);//合併後的檔案

    DirectoryInfo dicInfo = new DirectoryInfo(sourcePath);
    if (Directory.Exists(Path.GetDirectoryName(sourcePath)))
    {
        FileInfo[] files = dicInfo.GetFiles();
        foreach (FileInfo file in files.OrderBy(f => int.Parse(f.Name)))
        {
            FileStream addFile = new FileStream(targetPath, FileMode.Append, FileAccess.Write);
            BinaryWriter AddWriter = new BinaryWriter(addFile);

            //獲得上傳的分片資料流
            Stream stream = file.Open(FileMode.Open);
            BinaryReader TempReader = new BinaryReader(stream);
            //將上傳的分片追加到臨時檔案末尾
            AddWriter.Write(TempReader.ReadBytes((int)stream.Length));
            //關閉BinaryReader檔案閱讀器
            TempReader.Close();
            stream.Close();
            AddWriter.Close();
            addFile.Close();

            TempReader.Dispose();
            stream.Dispose();
            AddWriter.Dispose();
            addFile.Dispose();
        }
        DeleteFolder(sourcePath);
    }
}
/// <summary>
/// 刪除資料夾
/// </summary>
/// <param name="strPath"></param>
private static void DeleteFolder(string strPath)
{
    if (strPath != backuprecoveryfile)
    {
        Directory.Delete(strPath, true);
    }
    else
    {
        if (Directory.GetDirectories(strPath).Length > 0)
        {
            foreach (string fl in Directory.GetDirectories(strPath))
            {
                Directory.Delete(fl, true);
            }
        }
        //刪除這個目錄下的所有檔案
        if (Directory.GetFiles(strPath).Length > 0)
        {
            foreach (string f in Directory.GetFiles(strPath))
            {
                System.IO.File.Delete(f);
            }
        }
    }
}