1、搭建HTTP服务器
- 使用别人做好的HTTP服务器软件,一般作为资源服务器时使用该方式(学习阶段建议使用)
- 自己编写HTTP服务器应用程序,一般作为Web服务器或者短连接游戏服务器时使用该方式(工作后由后端程序员来做)
1.1 使用hfs搭建HTTP服务器,双击打开运行即可
1.2 添加服务器访问文件夹
右键打开添加文件夹
选择自己需要设置的访问文件夹
1.3 设置允许访问和上传权限
移到目标文件夹右键选择Properties
创建允许访问用户凭证
设置访问权限
设置上传权限
设置删除权限
2、C#的Http关键类介绍
2.1 HttpWebRequest类
重要方法
1)Create 创建新的WebRequest,用于进行HTTP相关操作
2)Abort 如果正在进行文件传输,用此方法可以终止传输
3)GetRequestStream 获取用于上传的流
4)GetResponse 返回HTTP服务器响应
5)Begin/EndGetRequestStream 异步获取用于上传的流
6)Begin/EndGetResponse 异步获取返回的HTTP服务器响应
重要成员
1)Credentials通信凭证,设置为NetworkCredential对象
2)PreAuthenticate是否随请求发送一个身份验证标头,一般需要进行身份验证时需要将其设置为true
3)Headers构成标头的名称/值对的集合
4)ContentLength发送信息的字节数上传信息时需要先设置该内容长度
5)ContentType在进行POST请求时,需要对发送的内容进行内容类型的设置
6)Method操作命令设置
- WebRequestMethods.Http类中的操作命令属性
- Get 获取请求,一般用于获取数据
- Post 提交请求,一般用于上传数据,同时可以获取
- Head 获取和Get一致的内容,只是只会返回消息头,不会返回具体内容
- Put 向指定位置上传最新内容
- Connect 表示与代理一起使用的HTTPCONNECT协议方法,该代理可以动态切换到隧道
- MkCol 请求在请求 URI(统一资源标识符)指定的位置新建集合
2.2 HttpWebResponse类
重要方法
1)Close:释放所有资源
2)GetResponseStream:返回从FTP服务器下载数据的流
重要成员
1)ContentLength:接受到数据的长度
2)ContentType:接受数据的类型
3)StatusCode:HTTP服务器下发的最新状态码
4)StatusDescription:HTTP服务器下发的状态代码的文本
5)BannerMessage:登录前建立连接时HTTP服务器发送的消息
6)ExitMessage:HTTP会话结束时服务器发送的消息
7)LastModified:HTTP服务器上的文件的上次修改日期和时间
2.3 NetworkCredential
3、Http下载数据
3.1 检测资源可用性
//利用Head请求参型,获取信息
//1.创建HTTP通讯用连接对象HttpWebRequest对象
HttpWebRequest req = HttpWebRequest.Create(new Uri("https://t7.baidu.com/it/u=2604797219,1573897854&fm=193&f=GIF")) as HttpWebRequest;
//2.设置请求类型或其它相关参数
req.Method = WebRequestMethods.Http.Head;
req.Timeout = 2000;
//3.发送请求,获取响应结果HttpWebResponse对象
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
//4.判断资源是否存在
if (res.StatusCode == HttpStatusCode.OK)
{
print("文件存在且可用");
print(res.ContentLength);
print(res.ContentType);
res.Close();
}
else
{
print("文件不可用" + res.StatusCode);
}
3.2 下载资源
//1.创建HTTP通讯用连接对象HttpWebRequest对象
HttpWebRequest req2 = HttpWebRequest.Create(new Uri("https://t7.baidu.com/it/u=2604797219,1573897854&fm=193&f=GIF")) as HttpWebRequest;
//2.设置请求类型或其它相关参数
req2.Method = WebRequestMethods.Http.Get;
req2.Timeout = 3000;
//3.发送请求,获取响应结果HttpWebResponse对象
HttpWebResponse res2 = req2.GetResponse() as HttpWebResponse;
//4.获取响应数据流,写入本地路径
if (res2.StatusCode == HttpStatusCode.OK)
{
print(Application.persistentDataPath);
using (FileStream fileStream = File.Create(Application.persistentDataPath + "/httpDownLoad.jpg"))
{
Stream downLoadStream = res2.GetResponseStream();
byte[] bytes = new byte[2048];
//读取数据
int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
//一点一点写入本地
while (contentLength > 0)
{
fileStream.Write(bytes, 0, contentLength);
contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
}
fileStream.Close();
downLoadStream.Close();
res2.Close();
}
print("下载成功");
}
else
{
print("下载失败" + res2.StatusCode);
}
3.3 Get请求类型携带额外信息
1)协议部分
2)域名或公网IP部分
3)端口部分(默认80不需要写)
4)虚拟目录部分
5)参数部分
//我们在进行HTTP通信时,可以在地址后面加一些额外参数传递给服务端
//一般在和短连接游戏服务器通讯时,需要携带额外信息
// 举例:
//http://www.aspxfans.com:8080/news/child/index.boardID=5&ID=24618&page=1
//这个链接可以分成几部分
//1.协议部分:取决于服务器端使用的哪种协议
//http: //一 普通的http超文本传输协议
//https: //一 加密的超文本传输协议
//2.域名部分:
//www.aspxfans.com
//也可以填写服务器的公网IP地址
//3.端口部分:
//8080
//可以不写,如果不写默认为80
//4.虚拟目录部分:
//news/child/
//域名后的/开始,到最后一个/之前的部分
//5.文件名部分:
// index.asp
//?之前的最后一个 / 后的部分
// 6.参数部分:
//boardID=5&ID=24618&page=1
//?之后的部分就是参数部分,多个参数一&分隔开
//这里有三个参数
//boardID = 5
//ID = 24618
//page = 1
//我们在和服务端进行通信时,只要按照这种规则格式进行通信,就可以传递参数给对象
//主要可用于:
//1.web网站服务器
//2.游戏短连接服务器
//等
4、Http上传文件
4.1 上传文件到HTTP资源服务器需要遵守的规则
//上传文件时内容的必备规则
// 1:ContentType ="multipart/form-data;boundary=边界字符串";
// 2:上传的数据必须按照格式写入流中
// --边界字符串
// Content - Disposition:form - data; name = "字段名字,之后写入的文件2进制数据和该字段名对应"; filename = "传到服务器上使用的文件名"
// Content - Type:application / octet - stream(由于我们传2进制文件所以这里使用2进制)
// 空一行
// (这里直接写入传入的内容)
// --边界字符串--
// 3:保证服务器允许上传
// 4:写入流前需要先设置ContentLength内容长度
4.2 上传文件
//1.创建HttpWebRequest对象
HttpWebRequest req = HttpWebRequest.Create("http://192.168.1.25:8000/HttpServer/") as HttpWebRequest;
//2.相关设置(请求类型,内容类型,超时,身份验证等)
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data;boundary=MrZhou";
req.Timeout = 50000;
req.Credentials = new NetworkCredential("ZT", "zt123");
req.PreAuthenticate = true;
//3.按格式拼接字符串并且转为字节数组之后用于上传
//3-1.文件数据前的头部信息
// --边界字符串
// Content-Disposition:form-data;name="字段名字,之后写入的文件2进制数据和该字段名对应";filename="传到服务器上
// Content-Type:application/octet-stream(由于我们传2进制文件所以这里使用2进制)
string head = "--MrZhou\r\n" +
"Content-Disposition:form-data;name=\"file\";filename=\"http上传的文件.jpg\"\r\n" +
"Content-Type:application/octet-stream\r\n\r\n";
//头部拼接字符串规则信息的字节数组
byte[] headBytes = Encoding.UTF8.GetBytes(head);
//3-2.结束的边界信息
// --边界字符串--
byte[] endBytes = Encoding.UTF8.GetBytes("\r\n--MrZhou--\r\n");
//4.写入上传流
//4-1.设置上传长度
//4-2.先写入前部分头部信息
//4-3.再写入文件数据
//4-4.在写入结束的边界信息
using (FileStream localFileStream = File.OpenRead(Application.streamingAssetsPath + "/test.png"))
{
//4-1.设置上传长度
//总长度 是前部分字符串 + 文件本身大小 + 后部分边界字符串
req.ContentLength = headBytes.Length + localFileStream.Length + endBytes.Length;
//用于上传的流
Stream upLoadStream = req.GetRequestStream();
//4-2.先写入前部分头部信息
upLoadStream.Write(headBytes, 0, headBytes.Length);
//4-3.再写入文件数据
byte[] bytes = new byte[2048];
int contentLength = localFileStream.Read(bytes, 0, bytes.Length);
while(contentLength > 0)
{
upLoadStream.Write(bytes, 0, contentLength);
contentLength = localFileStream.Read(bytes, 0, bytes.Length);
}
//4-4.在写入结束的边界信息
upLoadStream.Write(endBytes, 0, endBytes.Length);
upLoadStream.Close();
localFileStream.Close();
}
//5.上传数据,获取响应
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
if(res.StatusCode == HttpStatusCode.OK)
{
print("上传通讯成功");
}
else
{
print("上传失败" + res.StatusCode);
}
5、Http管理类
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
public class HttpMgr
{
private static HttpMgr instance = new HttpMgr();
public static HttpMgr Instance => instance;
//private string HTTP_PATH = "https://t7.baidu.com/it/";
private string HTTP_PATH = "http://192.168.1.25:8000/HttpServer/";
private string USER_NAME = "ZT";
private string PASS_WORD = "zt123";
/// <summary>
/// 下载指定文件到本地指定路径中
/// </summary>
/// <param name="url">远程文件路径</param>
/// <param name="localPath">本地路径</param>
/// <param name="action">下载结束后的回调函数</param>
public async void DownLoadFile(string url, string localFilePath, UnityAction<HttpStatusCode> action = null)
{
HttpStatusCode result = HttpStatusCode.OK;
await Task.Run(() =>
{
try
{
//判断文件是否存在 Head
//1.创建HTTP连接对象
HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;
//2.设置请求类型 和 其他相关参数
req.Method = WebRequestMethods.Http.Head;
req.Timeout = 2000;
//3.发送请求
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
//存在才下载
if(res.StatusCode == HttpStatusCode.OK)
{
res.Close();
//下载文件
//1.创建HTTP连接对象
req = HttpWebRequest.Create(url) as HttpWebRequest;
//2.设置请求类型 和 其他相关参数
req.Method = WebRequestMethods.Http.Get;
req.Timeout = 2000;
//3.发送请求
res = req.GetResponse() as HttpWebResponse;
//4.存储数据到本地
if(res.StatusCode == HttpStatusCode.OK)
{
//存储数据
using (FileStream fileStream = File.Create(localFilePath))
{
Stream downLoadStream = res.GetResponseStream();
byte[] bytes = new byte[2048];
int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
while (contentLength > 0)
{
fileStream.Write(bytes, 0, contentLength);
contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
}
fileStream.Close();
downLoadStream.Close();
}
result = HttpStatusCode.OK;
}
else
{
result = res.StatusCode;
}
}
else
{
result = res.StatusCode;
}
res.Close();
}
catch (WebException e)
{
result = HttpStatusCode.InternalServerError;
Debug.LogError("文件下载失败" + e.Message + e.Status);
}
});
action?.Invoke(result);
}
/// <summary>
/// 上传文件
/// </summary>
/// <param name="fileName">传到远端服务器上的文件名</param>
/// <param name="localFilePath">本地的文件路径</param>
/// <param name="action">上传结束后的回调函数</param>
public async void UpLoadFile(string fileName, string localFilePath, UnityAction<HttpStatusCode> action = null)
{
HttpStatusCode result = HttpStatusCode.BadGateway;
await Task.Run(() =>
{
try
{
HttpWebRequest req = HttpWebRequest.Create(HTTP_PATH) as HttpWebRequest;
req.Method = WebRequestMethods.Http.Post;
req.ContentType = "multipart/form-data;boundary=MrZhou";
req.Timeout = 50000;
req.Credentials = new NetworkCredential(USER_NAME, PASS_WORD);
req.PreAuthenticate = true;
//拼接字符串头部
string head = "--MrZhou\r\n" +
"Content-Disposition:form-data;name=\"file\";filename=\"{0}\"\r\n" +
"Content-Type:application/octet-stream\r\n\r\n";
//替换文件名
head = string.Format(head, fileName);
Debug.Log(head);
byte[] headBytes = Encoding.UTF8.GetBytes(head);
//尾部的边界字符串
byte[] endBytes = Encoding.UTF8.GetBytes("\r\n--MrZhou--\r\n");
using(FileStream localStream = File.OpenRead(localFilePath))
{
//设置长度
req.ContentLength = headBytes.Length + localStream.Length + endBytes.Length;
//写入流
Stream upLoadStream = req.GetRequestStream();
//写入头部
upLoadStream.Write(headBytes, 0, headBytes.Length);
//写入上传文件
byte[] bytes = new byte[2048];
int contentLength = localStream.Read(bytes, 0, bytes.Length);
while (contentLength > 0)
{
upLoadStream.Write(bytes, 0, contentLength);
contentLength = localStream.Read(bytes, 0, bytes.Length);
}
//写入尾部
upLoadStream.Write(endBytes, 0, endBytes.Length);
upLoadStream.Close();
localStream.Close();
}
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
//让外部去处理结果
result = res.StatusCode;
res.Close();
}
catch (WebException e)
{
Debug.LogError("上传失败" + e.Message + e.Status);
}
});
action?.Invoke(result);
}
}