目录导航:
前言:
什么是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 手机号登陆注册
该设计的思路是每个手机号对应一个用户,手机号为必填项。
流程:
首先输入手机号,然后发送到服务端。先判断该手机号是否存在账号,如果没有,就会生成随机验证码,将手机号和验证码绑定到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.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形式的请求参数,最后在坚持不懈的尝试下终于成功了。