.Net使用HttpClient以multipart/form-data形式post上傳文件及其相關參數

  目錄導航:
  
  前言:
  
  什麼是multipart/form-data請求:
  
  Html上傳圖片按鈕:
  
  使用ajax將圖片文件流和相關參數傳遞到後端進行拼接:
  
  後端接收圖片和參數,並將圖片文件流轉化爲圖片字節類型數據:
  
  重點,HttpClient拼接multipart/form-data形式參數post提交數據:
  
  使用Fiddler 4 抓包查看請求的參數:
  
  總結:
  
  文章正文:
  
  回到頂部
  
  前言:
  
  本次要講的是使用.Net HttpClient拼接multipark/form-data形式post上傳文件和相關參數,並接收到上傳文件成功後返回過來的結果(圖片地址,和是否成功)。可能有不少人會說用ajax不是就能夠輕鬆的實現嗎?的確是在不存在跨域問題的前提下使用ajax上傳文件,接收返回結果是最佳的選擇。無奈的是咱們對接的是第三方的一個上傳圖片的接口,並且對方並無對咱們的域名設置容許跨域,爲了可以解決這一問題咱們只可以經過後端請求避免跨域問題。
  
  回到頂部
  
  什麼是multipart/form-data請求:
  
  關於multipart/form-data詳情查看: https://www.cnblogs.com/tylerdonet/p/5722858.html
  
  回到頂部
  
  Html上傳圖片按鈕:
  
  <div class="cover-hd">
  
  <a href="javascript:;" class="a-uploadCustom">
  
  <input type="file" id="Logoimg" onchange="OnchangeImage(this)" /></a>
  
  </div>
  
  回到頂部
  
  使用ajax將圖片文件流和相關參數傳遞到後端進行拼接:
  
  注意:由於我這裏調用第三方接口須要傳遞(appid應用程序惟一標識,random隨機數,和sign簽名)
  
  複製代碼
  
  <script type="text/javascript">
  
  //後端圖片上傳
  
  function OnchangeImage(obj) {
  
  var formData = new FormData();
  
  var files = $(obj).prop('files'); //獲取到文件列表
  
  console.log(files[0]);
  
  formData.append("imgType", 1);
  
  formData.append("appId","你須要傳遞的參數");
  
  formData.append("random", "你須要傳遞的參數");
  
  formData.append("file", files[0]);//圖片文件流
  
  formData.append("sign", "你須要傳遞的參數");
  
  console.log(formData);
  
  jQuery.support.cors = true;
  
  $.ajax({
  
  async: true,
  
  contentType: false, //頭部請求內容格式
  
  dataType: 'json',
  
  type: 'post',
  
  data:formData,
  
  // 告訴jQuery不要去處理髮送的數據
  
  processData: false,
  
  url: "@Url.Action("ImageUpload", "MtVirtualStore")",//後端接收圖片接口
  
  success: function(data) {
  
  //後端Httpclient請求成功後返回過來的結果
  
  console.log(data);
  
  }
  
  });
  
  }
  
  </script>
  
  複製代碼
  
  如今幾乎大部分的App都支持使用多個第三方帳號進行登陸,如:微信、QQ、微博等,咱們把此稱爲多帳號統一登錄。而這些帳號的表設計,流程設計相當重要,否則後續擴展性賊差。本文不提供任何代碼實操,可是梳理一下博主根據我司帳號模塊的設計,提供思路,僅供參考。
  
  1、 自建的登錄體系
  
  1.1 手機號登錄註冊
  
  該設計的思路是每一個手機號對應一個用戶,手機號爲必填項。
  
  流程:
  
  首先輸入手機號,而後發送到服務端。先判斷該手機號是否存在帳號,若是沒有,就會生成隨機驗證碼,將手機號和驗證碼綁定到Redis中,並設置必定的過時時間(過時時間通常是5分鐘,這就是咱們通常手機驗證碼的有效期),最後將驗證碼經過短信發送給用戶。
  
  用戶接收到驗證碼後,在界面填寫驗證碼以及密碼等基礎信息,而後將這些數據發送服務端。服務端收到後,先判斷在Redis裏面這個手機號對應的驗證碼是否一致,,失敗就返回錯誤碼,成功就給用戶建立一個帳號和保存密碼。
  
  註冊成功後,用戶便可經過本身的手機號+密碼進行登錄。
  
  問題:
  
  用戶體驗差,須要完成獲取驗證碼,填寫驗證碼/密碼/用戶名等諸多的信息完成註冊,而後才能使用;
  
  容易遺忘密碼,遺忘後,只能經過忘記密碼來從新設置密碼。
  
  1.2 優化註冊登錄
  
  該方案的思路是弱化密碼的必填性,即不管用戶是否註冊過,可經過手機號 + 驗證碼 直接進行登錄(保留手機號 + 密碼登陸的方式)。
  
  流程:
  
  輸入手機號,而後發送到服務端。服務端生成隨機驗證碼,將手機號和驗證碼綁定到Redis中,並設置必定的過時時間(過時時間通常是5分鐘,這就是咱們通常手機驗證碼的有效期),最後將驗證碼經過短信發送給用戶。
  
  用戶接收到驗證碼後,在界面只需填寫收到的驗證碼,提交到服務端。服務端收到後,先判斷在Redis裏面這個手機號對應的驗證碼是否一致,失敗就返回錯誤碼,成功就直接登陸。若是是老用戶,直接拉取用戶信息;若是是新用戶,則提示他能夠完善用戶信息(不強制)。
  
  用戶經過手機號 + 驗證碼登陸後,也可選擇設置密碼,而後就能夠經過手機號 + 密碼的方式登陸,即:密碼是非必填項。
  
  用戶表設計:
  
  id    user_name    user_password    user_mobile    state    more
  
  用戶id    用戶名    用戶密碼    手機號碼    帳號狀態    其餘信息
  
  1.3 引入第三方帳戶方案
  
  1.3.1 微博登陸
  
  進入 Web2.0 時代 ,微博開放了第三方網站登陸, 產品說, 這個咱們得要, 加個用微博賬號就能登陸咱們的App吧,並且得和咱們本身的用戶表關聯。
  
  流程:
  
  客戶端調用微博登陸的界面,進行輸入用戶名、密碼,登陸成功後,會返回access_token,經過access_token調取API接口獲取用戶信息。
  
  服務端經過用戶信息在咱們用戶表建立一個帳號,之後,該第三方帳號便可經過該微博帳號直接進行登錄。
  
  微博用戶信息表設計:
  
  id    user_id    uid    access_token
  
  主鍵id    用戶id    微博惟一id    受權碼
  
  1.3.2 噩夢來臨
  
  緊接着, QQ又開放用戶登陸了, 微信開放用戶登陸了,網易開發用戶登陸了。。。。。。一會兒要接入好多家第三方登陸了, 只能按照 「微博用戶信息表」 新建一個表,重寫一套各個第三方登陸。
  
  2、 優化帳號體系
  
  2.1 原帳號體系分析
  
  自建登錄體系:不管 手機號 + 密碼 , 仍是 手機號 + 驗證碼 , 都是一種 用戶信息+密碼 的驗證形式;
  
  第三方登陸:也是用戶信息+密碼 的形式, 用戶信息即第三方系統中的 ID(第三方系統中的惟一標識), 密碼即 access_token, 只不過是一種有使用時效按期修改的密碼。
  
  回到頂部
  
  後端接收圖片和參數,並將圖片文件流轉化爲圖片字節類型數據:
  
  複製代碼
  
  //接收前端圖片文件信息
  
  [HttpPost]
  
  public JsonResult ImageUpload(FormContext context)
  
  {
  
  HttpPostedFileBase fileData = Request.Files[0];
  
  string appId=Request["appId"];
  
  string random=Request["random"];
  
  string sign=Request["sign"];
  
  string imgType=Request[www.jintianxuesha.com"imgType"];
  
  if (fileData != null)
  
  {
  
  try{
  
  string fileName = Path.GetFileName(fileData.FileName);//原始文件名稱
  
  byte[] byteFileData = ReadFileBytes(fileData);//文件流轉爲字節流
  
  var resultContext =HttpClientPostUpload(byteFileData,appId,random,sign,imgType, fileName);
  
  return Json(new { code = 1, list = resultContext,msg="上傳成功~"});
  
  }
  
  catch (Exception ex)
  
  {
  
  return Json(new { code = 0, msg = ex.Message });
  
  }
  
  }
  
  else
  
  {
  
  return Json(new { code = 0, msg = "圖片上傳失敗,請稍後再試~" });
  
  }
  
  }
  
  //文件流轉化爲字節
  
  /// <summary>
  
  /// 文件流類型轉化字節類型
  
  /// </summary>
  
  /// <param name="fileData"www.seocelve.com>文件流數據</param>
  
  /// <returns></returns>
  
  private byte[] ReadFileBytes(www.haishenyul.com HttpPostedFileBase fileData)
  
  {
  
  byte[] data;
  
  using (Stream inputStream = fileData.InputStream)
  
  複製代碼
  
  回到頂部
  
  重點,HttpClient拼接multipart/form-data形式參數post提交數據:
  
  複製代碼
  
  /// <summary>
  
  /// 向目標地址提交圖片文件參數數據
  
  /// </summary>
  
  /// <param name="bmpBytes">圖片字節流</param>
  
  /// <param name="appId">appid</param>
  
  /// <param name="random">隨機數</param>
  
  /// <param name="sign">簽名</param>
  
  /// <param name="imgType">上傳圖片類型</param>
  
  /// <param name="fileName">圖片名稱</param>
  
  /// <returns></returns>
  
  public string HttpClientPostUpload(byte [www.pingguoyul.cn bmpBytes, string appId, string random,,string sign,string imgType,string fileName)
  
  {
  
  using (var client = new HttpClient())
  
  {
  
  List<ByteArrayContent>www.yuanhuapt.cn  list = new List<ByteArrayContent>();
  
  var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(appId));
  
  dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")//內容處置標頭
  
  {
  
  Name = "appId"
  
  };
  
  list.Add(dataContent);
  
  var dataContent2 = new ByteArrayContent(Encoding.UTF8.GetBytes(imgType));
  
  dataContent2.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
  
  {
  
  Name = "imgType"
  
  };
  
  list.Add(dataContent2)www.yasenyuLee.cn;
  
  var dataContent3 www.jiuhaoyulept.com= new ByteArrayContent(Encoding.UTF8.GetBytes(random));
  
  dataContent3.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
  
  {
  
  Name = "random"
  
  };
  
  list.Add(dataContent3);
  
  var dataContent4 = new ByteArrayContent(Encoding.UTF8.GetBytes(sign));
  
  dataContent4.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
  
  {
  
  Name = "sign"
  
  };
  
  list.Add(dataContent4);
  
  List<ByteArrayContent>www.yasenyuLe.com list2 = new List<ByteArrayContent>();
  
  var fileContent = new ByteArrayContent(bmpBytes);//填充圖片字節
  
  fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
  
  {
  
  Name="file",
  
  FileName=fileName
  
  list.Add(fileContent)
  
  using (var content =new MultipartFormDataContent())
  
  Action<List<ByteArrayContent>> act =www.tongyayuLe.cn (dataContents) =>
  
  {//聲明一個委託,該委託的做用就是將ByteArrayContent集合加入到MultipartFormDataContent中
  
  foreach (var byteArrayContent in dataContents)
  
  act(list);//執行act
  
  try
  
  複製代碼
  
  回到頂部
  
  使用Fiddler 4 抓包查看請求的參數:
  
  由於咱們沒有辦法看到咱們所拼接成功後的multipark/form-data形式的數據,想要看到對應拼接的請求參數可使用 Fiddler 4 抓包工具查看:
  
  關於Fiddler 4抓包工具的使用能夠閱讀該篇博客:https://www.zongxyuLe.com  /p/55f7be58a7e4
  
  抓包獲取到的multipark/form-data形式的請求參數以下圖:
  
  回到頂部
  
  總結:
  
  寫到最後才發現,本來只須要一個簡單的請求就能夠解決的問題由於跨域把這個問題變得如此繁瑣,搞得真叫人蛋痛。這裏我試過了不少種方式拼接multipark/form-data形式的請求參數,最後在堅持不懈的嘗試下終於成功了。javascript

相關文章
相關標籤/搜索