Unity 大文件上傳下載 (WebApi) 動態添加mimetype

下載比較簡單,只要有個網頁路徑就行  網頁路徑示例:http://192.168.1.21:8013/Data/LocationSystem.exe

但是上傳不行,客戶端上傳之後,服務端需要設置接收方法。把接收數據保存到服務端

 

在IIS上掛載一個網站,裏面鏈接到我們上傳下載的文件夾

 URI:http://192.168.1.21:8013/Data     本地路徑:C:\Users\Administrator\Desktop\網頁所在文件夾\Data

1.客戶端

項目內使用了BestHttpPro插件,所以默認用了這個插件,去實現上傳和下載的功能

A.使用BestHttp下載文件:

   [ContextMenu("Download")]
    public void StartDownload()
    {
        var request = new HTTPRequest(new System.Uri("http://192.168.1.21:8013/Data/LocationSystem.exe"), (req, resp) => {
            //下載完成之後執行

           List<byte[]> fragments = resp.GetStreamedFragments();

            // 把下載的文件保存到E盤根目錄
            using (FileStream fs = new FileStream(@"E:\LocationSystem1.exe", FileMode.Append))
                foreach (byte[] data in fragments) fs.Write(data, 0, data.Length);
            if (resp.IsStreamingFinished) Debug.Log("Download finished!");
        });
        request.UseStreaming = true;
        request.StreamFragmentSize = 1 * 1024 * 1024;// 1 megabyte
        request.DisableCache = true;// already saving to a file, so turn off caching
        request.OnProgress = OnLoadProgress;
        request.Send();
    }

  //下載進度

   void OnLoadProgress(HTTPRequest request, int download, int length)
    {
        float progressPercent = (download / (float)length) * 100.0f;
        Debug.Log("Download: " + progressPercent.ToString("F2") + "%");
    }

B:使用BestHttp上傳文件:

[ContextMenu("UploadRawData")]
    public void UploadSecond()
    {
        StartCoroutine(UploadFiles());
    }
    public IEnumerator UploadFiles()
    {
        string serverUrl = "http://localhost:8733/api/fileTransfer/uploadFiles";
        var request = new HTTPRequest(new System.Uri(serverUrl), HTTPMethods.Post, OnFinished);
        string fileName = @"E:\xxx.txt";
        byte[] data = FileContent(fileName);
        string fileName2 = @"E:\LocationSystem1.exe";
        byte[] data2 = FileContent(fileName2);        
        request.AddBinaryData("BigFile", data, "xxxttttt1.txt");
        request.AddBinaryData("BigFile", data2, "xxxeeeeee2.exe");
        request.SetHeader("Content-Type", "application/octet-stream");
        request.OnUploadProgress = OnUploadProgress;
        request.DisableCache = true;
        request.Send();
        yield return null;
    }

void OnFinished(HTTPRequest originalRequest, HTTPResponse response)
    {
        Debug.Log("finished...");
        Debug.Log("Response:"+response.DataAsText);
    }

    void OnUploadProgress(HTTPRequest request, long uploaded, long length)
    {
        float progressPercent = (uploaded / (float)length) * 100.0f;
        Debug.Log("Uploaded: " + progressPercent.ToString("F2") + "%");
    }

C:使用UnityWebRequest上傳文件(對應的,也可以使用這個下載文件)
    [ContextMenu("PostWebapi")]
    public void StartPost()
    {
        StartCoroutine(PostObject("http://localhost:8733/api/fileTransfer/uploadFiles", null, null));
    }

    public IEnumerator PostObject(string url, Action callback, Action<string> errorCallback)
    {
        string[] path = new string[2];
        path[0] = @"E:\ZTest\xxx.txt";
        path[1] = @"E:\ZTest\xxxeee.txt";

        WWWForm form = new WWWForm();
        for (int i = 0; i < path.Length; i++)
        {
            byte[] uploadData = FileContent(path[i]);
            form.AddBinaryData("files[]", uploadData, Path.GetFileName(path[i]));
        }
        using (UnityWebRequest www = UnityWebRequest.Post(url, form))
        {
            yield return www.SendWebRequest();
            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log("錯誤:www.error =>" + www.error);
                if (errorCallback != null)
                {
                    errorCallback(www.error);
                }
            }
            else
            {
                // Show results as text
                string results = www.downloadHandler.text;
                Debug.Log("Result:" + results);
                if (callback != null) callback();
            }
        }
    }

  上面兩種上傳的方式,都用下面的方法,把文件讀取成byte[]
    private byte[] FileContent(string fileName)
    {    
        using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            try
            {
                byte[] buffur = new byte[fs.Length];
                fs.Read(buffur, 0, (int)fs.Length);
                return buffur;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
   

2.服務端

服務端接收客戶端上傳的文件,需要設置一下最大接收Size。否則在傳輸大文件時,WeApi接收方法不會觸發

設置如下:

找到啓動WebApi的代碼,設置MaxBufferSize和MaxRecievedMessageSize(這兩個默認只有幾十兆)

如果是.net 做的網頁,去web.Config裏設置,這一類能搜到很多資料。想反,我們在wpf中使用webapi傳輸數據的,資料還少點。

上面的設置完成後,新建一個FileTransferController用於接收客戶端傳輸的文件

[RoutePrefix("api/fileTransfer")]
    public class FileTransferController : ApiController
    {
        private string saveDirectoryPath = "";

        [Route("uploadFiles")]
        [HttpPost]
        public IHttpActionResult GetUploadFile()
        {

            //把文件保存路徑放在了App.config文件中了。打包後也可以動態修改。

           //如果只是測試,給個默認路徑saveDirectoryPath =@"E:\yourSaveDirectory"
            if(string.IsNullOrEmpty(saveDirectoryPath))
            {
                //去除路徑前後空格
                saveDirectoryPath = AppSetting.FileTransferSaveDirctory.Trim();
                //去除文件夾最後的\  例如:E:\SaveTest\==》 E:\SaveTest
                if (!string.IsNullOrEmpty(saveDirectoryPath) && saveDirectoryPath.EndsWith(@"\"))
                {
                    saveDirectoryPath = saveDirectoryPath.Remove(saveDirectoryPath.Length - 1);
                }
            }
            //判斷文件夾是否存在
            if (Directory.Exists(saveDirectoryPath) == false)
            {
                //創建用於存圖片的文件夾
                Directory.CreateDirectory(saveDirectoryPath);
            }

            //準備接收文件
            var provider = new MultipartMemoryStreamProvider();
            IEnumerable<HttpContent> parts = null;
            //異步
            Task.Factory.StartNew(() => parts = Request.Content.ReadAsMultipartAsync().Result.Contents, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).Wait();
            foreach (var item in parts)
            {
                //判斷是否有文件
                if (item.Headers.ContentDisposition.FileName == null)
                    continue;
                var ms = item.ReadAsStreamAsync().Result;
                using (var br = new BinaryReader(ms))
                {
                    if (ms.Length <= 0)
                        break;
                    var data = br.ReadBytes((int)ms.Length);

                    //FileName對應客戶端AddBinaryData中的FileName
                    string fileName = item.Headers.ContentDisposition.FileName.Replace("\"", "");
                    string newPath = saveDirectoryPath+@"\"+ fileName;
                    if (File.Exists(newPath)) newPath = GetFileSavePath(1,newPath);
                    File.WriteAllBytes(newPath, data);
                }
            }
            return Json<dynamic>(new { Result = "Data upload success!" });
        }
        /// <summary>
        /// 如有重複文件,則後綴+(1)  locaiton.sql->location(1).sql
        /// </summary>
        /// <param name="currentIndex"></param>
        /// <param name="filePath"></param>
        /// <param name="suffixName"></param>
        /// <returns></returns>
        private static string GetFileSavePath(int currentIndex, string filePath)
        {
            try
            {                
                string infoName = string.Format("{0}({1})",Path.GetFileNameWithoutExtension(filePath),currentIndex);
                string fileName = string.Format(@"{0}\{1}{2}", Path.GetDirectoryName(filePath), infoName ,Path.GetExtension(filePath));
                if (File.Exists(fileName))
                {
                    currentIndex++;
                    return GetFileSavePath(currentIndex, filePath);
                }
                else
                {
                    return fileName;
                }
            }catch(Exception e)
            {
                Log.Info("FileTransferController.Exception:"+e.ToString());
                return filePath;
            }
            
        }
    }

下載時碰到的問題:

像後綴名爲 .unitypackage 這樣的文件,下載時會報404 not found 錯誤。原因是這種類型的後綴,沒在網頁mimeType中添加。

解決方案:

       1.在IIS中,找到對應網站,點擊新增mimeType.   擴展名設置 *(*號代表支持任何文件)  ,mimeType設置成 application/octet-stream;

2.如果不想讓網頁支持所有後綴,可以使用代碼,去單獨註冊後綴

之前找資料,給的網站Index 都默認爲1 .後面找到根據網站名稱,獲取對應index的方法。

下面代碼,需要在nguget管理中,添加 System.DirectoryServices.dll

IISOle,需要右鍵引用-添加服務引用,找到Com類庫,添加 ActiveDSIISNamespaceProvider

 private void Button_Click(object sender, RoutedEventArgs e)
        {

            //zs是我網站的名稱
            string iisSite = string.Format("IIS://localhost/W3SVC/{0}/Root",GetWebIndex("zs"));
            SetMimeTypeProperty(iisSite, ".json", "application/octet-stream");
        }
        /// <summary>
        /// 根據網站名,獲取Index
        /// </summary>
        /// <param name="websiteName"></param>
        /// <returns></returns>
        private string GetWebIndex(string websiteName)
        {
            DirectoryEntry root = new DirectoryEntry("IIS://localhost/W3SVC");
            foreach (DirectoryEntry dir in root.Children)
            {
                if (dir.SchemaClassName == "IIsWebServer")
                {
                    string ww = dir.Properties["ServerComment"].Value.ToString();
                    if(ww==websiteName)
                    {
                        return dir.Name;
                    }
                }
            }
            return "1";
        }
        /// <summary>
        /// 設置IIS的MIME類型,SetMimeTypeProperty("IIS://localhost/W3SVC/1/Root", ".hlp", "application/winhlp");
        /// </summary>
        /// <param name="metabasePath"></param>
        /// <param name="newExtension"></param>
        /// <param name="newMimeType"></param>
        static void SetMimeTypeProperty(string metabasePath, string newExtension, string newMimeType)
        {           
            try
            {
                DirectoryEntry path = new DirectoryEntry(metabasePath);
                PropertyValueCollection propValues = path.Properties["MimeMap"];
                Console.WriteLine(" Old value of MimeMap has {0} elements", propValues.Count);

                object exists = null;
                foreach (object value in propValues)
                {
                    // IISOle requires a reference to the Active DS IIS Namespace Provider in Visual Studio .NET
                    IISOle.IISMimeType mimetypeObj = (IISOle.IISMimeType)value;
                    Console.WriteLine("  {0}->{1}", mimetypeObj.Extension, mimetypeObj.MimeType);
                    if (newExtension == mimetypeObj.Extension)
                        exists = value;
                }

                if (null != exists)
                {
                    propValues.Remove(exists);
                    Console.WriteLine(" Found an entry for {0}; removing it before adding the new one.", newExtension);
                }

                IISOle.MimeMapClass newObj = new IISOle.MimeMapClass();
                newObj.Extension = newExtension;
                newObj.MimeType = newMimeType;
                propValues.Add(newObj);
                path.CommitChanges();
                Console.WriteLine(" Done.");

            }
            catch (Exception ex)
            {
                if ("HRESULT 0x80005006" == ex.Message)
                    Console.WriteLine(" Property MimeMap does not exist at {0}", metabasePath);
                else
                    Console.WriteLine("Failed in SetMimeTypeProperty with the following exception: \n{0}", ex.Message);
            }
        }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章