Win11系统,使用c#控制台应用程序
1. IIS资源服务器部署
网上有很多教程这里提供一些教程,比我写的好
- 什么是IIS
- 开启IIS+部署 1
- 开启IIS+部署 2
- 开启IIS+部署 3
- 如何查看ip
- 如何查看被占用的端口号
- 防火墙限制,防火墙设置起来比较麻烦,可以直接关闭防火墙
- 以上教程基本上能部署好资源服务器,配置过程中,尽量不要设置用户和密码,以及ftp配置SSL验证,不然问题还是比较多的
2. Http资源下载,支持断点续传
由于http服务器没有研究明白,调资源上传功能一直没有调通,可以使用Ftp上传代替,网上有很多布置在资源服务器上的上传代码可以参考,整个网页上茶资源也不错
脚本名称: HttpFileUtil.cs
2.1 获取资源下载大小
/// <summary>
/// 获取文件大小
/// </summary>
/// <param name="url"></param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static long GetDownloadFileSize(string url, string userName = "", string password = "")
{
try
{
url = HttpUtility.UrlPathEncode(url);
var req = (HttpWebRequest)WebRequest.Create(url);
req.Method = WebRequestMethods.Http.Head;
req.Credentials = new NetworkCredential(userName, password);
req.Timeout = 1000;
req.Credentials = new NetworkCredential(userName, password);
var res = (HttpWebResponse)req.GetResponse();
long rl = 0;
if (res.StatusCode == HttpStatusCode.OK)
{
rl = res.ContentLength;
}
res.Close();
return rl;
}
catch { }
return 0;
}
2.1 资源下载,并且支持断点续传
/// <summary>
/// 下载文件(支持断点续下)
/// </summary>
/// <param name="url"></param>
/// <param name="desDir">本地文件夹路径</param>
/// <param name="overwrite">覆盖下载</param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <param name="callback">进度回调</param>
/// <returns></returns>
public static bool DownloadFile(string url, string desDir,bool overwrite = false, string userName = "", string password = "",Action<long,long> callback = null)
{
string fileName = Path.GetFileName(url);
if (string.IsNullOrEmpty(desDir))
{
//路径为空,设置文件名
desDir = fileName;
}
else
{
if (!Directory.Exists(desDir))
{
//文件夹不存在,创建新文件夹
Directory.CreateDirectory(desDir);
}
//路径连接
desDir = Path.Combine(desDir, fileName);
}
//编码Url
url = HttpUtility.UrlPathEncode(url);
long downloadFileSize = GetDownloadFileSize(url, userName, password);
if (downloadFileSize <= 0)
{
//下载文件异常
callback?.Invoke(-1, -1);
return false;
}
bool flag = false;
long curPosition = 0;
Stream stream = null;
FileStream fileStream = null;
//下载未完成之前存为临时文件(支持断点续下)
string tmpFile = desDir + ".temp";
try
{
//下载文件已存在
if (File.Exists(desDir))
{
var fileInfo = new FileInfo(desDir);
curPosition = fileInfo.Length;
if (curPosition >= downloadFileSize)
{
//覆盖进行删除重新下载
if (overwrite)
{
File.Delete(desDir);
}
else
{
callback?.Invoke(downloadFileSize, downloadFileSize);
return true;
}
}
}
}
catch
{
}
try
{
//临时文件存在
if (File.Exists(tmpFile))
{
fileStream = File.OpenWrite(tmpFile);
curPosition = fileStream.Position;
if (curPosition >= downloadFileSize)
{
//不覆盖
if (!overwrite)
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
//重命名
File.Move(tmpFile, desDir);
callback?.Invoke(downloadFileSize, downloadFileSize);
return true;
}
curPosition = 0;
}
callback?.Invoke(curPosition, downloadFileSize);
fileStream.Seek(curPosition, SeekOrigin.Current);
}
else
{
fileStream = new FileStream(tmpFile, FileMode.Create, FileAccess.Write);
curPosition = 0;
}
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
httpWebRequest.Credentials = new NetworkCredential(userName, password);
if (curPosition > 0)
{
//断点续下,将请求下载的字节流下载位置设置成上次下载位置
httpWebRequest.AddRange(curPosition);
}
long downloadSize = curPosition;
var res = httpWebRequest.GetResponse();
string totalLength = FormatSize(res.ContentLength, 2);
stream = res.GetResponseStream();
byte[] buffer = new byte[4096];
int byteReader = 0;
do
{
//读取, 写入本地
byteReader = stream.Read(buffer, 0, buffer.Length);
downloadSize += byteReader;
callback?.Invoke(curPosition, downloadFileSize);
fileStream.Write(buffer, 0, byteReader);
}
while (byteReader != 0);
//下载完成
callback?.Invoke(downloadFileSize, downloadFileSize);
res.Close();
flag = true;
}
catch (Exception e)
{
callback?.Invoke(-1, -1);
}
finally
{
if (fileStream != null)
{
fileStream.Flush();
fileStream.Close();
fileStream.Dispose();
}
if (stream != null)
{
stream.Dispose();
stream.Close();
}
if (flag)
{
//临时文件重命名
File.Move(tmpFile, desDir);
}
}
return flag;
}
3. Ftp资源上传下载,支持断点续传
Ftp实现功能包含资源: 上传(支持断点续传), 下载(支持断点续下) , 修改名称,删除; 文件夹:创建,删除,修改名称,也是根据网上的案例惊醒修改的
脚本名称: HttpFileUtil.cs
3.1 检测ftp是否可连接
/// <summary>
/// 检测ftp是否登录
/// </summary>
/// <param name="url"></param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static bool CheckFtp(string url, string userName = "", string password = "")
{
try
{
//编码Url
url = HttpUtility.UrlPathEncode(url);
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(url);
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(userName, password);
request.Timeout = 3000;//3秒超时
FtpWebResponse ftpWebResponse = (FtpWebResponse)request.GetResponse();
Stream responseStream = ftpWebResponse.GetResponseStream();
responseStream.Close();
ftpWebResponse.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
3.2 获取文件大小
/// <summary>
/// 获取文件大小
/// </summary>
/// <param name="url"></param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static long GetDownloadFileSize(string url, string userName = "", string password = "")
{
try
{
url = HttpUtility.UrlPathEncode(url);
FtpWebRequest ftpWebRequest = (FtpWebRequest)FtpWebRequest.Create(url);
ftpWebRequest.Credentials = new NetworkCredential(userName, password);
ftpWebRequest.Method = WebRequestMethods.Ftp.GetFileSize;
ftpWebRequest.UseBinary = true;
FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
Stream stream = ftpWebResponse.GetResponseStream();
long fileSize = ftpWebResponse.ContentLength;
stream.Close();
ftpWebResponse.Close();
return fileSize;
}
catch (Exception ex)
{
}
return 0;
}
3.3 创建文件夹,支持多级目录创建
创建文件夹包含,一级文件夹和多级文件夹,针对多级文件夹的,需要逐级文件夹进行创建
/// <summary>
/// 创建文件夹
/// </summary>
/// <param name="url"></param>
/// <param name="newDir"></param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static bool MaskDirectory(string url, string newDir, string userName = "", string password = "")
{
//创建新文件夹为空
if (string.IsNullOrEmpty(newDir))
{
return true;
}
string targetUrl = HttpUtility.UrlPathEncode(url + "/" + newDir);
//检测远端文件夹是否存在
bool check = CheckFtp(targetUrl, userName, password);
if (check)
{
//存在
return true;
}
//多级文件夹处理(例如:x/xx/xxx/xxxx)
string[] dirs = newDir.Replace("\\", "/").Split('/');
int length = dirs.Length;
if (length <= 1)
{
//一级文件夹,直接创建
return _MaskDirectory(url, targetUrl, userName, password);
}
//多级文件夹,分级创建,
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < length; i++)
{
if (string.IsNullOrEmpty(dirs[i]))
{
continue;
}
stringBuilder.Append(dirs[i]);
targetUrl = stringBuilder.ToString();
//检查当前文件夹是否存在
check = CheckFtp(url + "/" + targetUrl, userName, password);
if (check)
{
continue;
}
//进行创建
bool flag = _MaskDirectory(url, targetUrl, userName, password);
if (!flag)
{
return false;
}
stringBuilder.Append("/");
}
return true;
}
上面是处理多级文件夹,下面是ftp新建文件夹实现
/// <summary>
/// 创建文件夹
/// </summary>
/// <param name="url"></param>
/// <param name="newDir">新文件夹</param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
private static bool _MaskDirectory(string url, string newDir, string userName = "", string password = "")
{
if (string.IsNullOrEmpty(newDir))
{
return true;
}
try
{
url = HttpUtility.UrlPathEncode(url + "/" + newDir);
FtpWebRequest ftpWebRequest = (FtpWebRequest)FtpWebRequest.Create(url);
ftpWebRequest.Method = WebRequestMethods.Ftp.MakeDirectory;
ftpWebRequest.KeepAlive = false;
ftpWebRequest.UseBinary = true;
ftpWebRequest.Credentials = new NetworkCredential(userName, password);
FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
Stream stream = ftpWebResponse.GetResponseStream();
stream.Close();
ftpWebResponse.Close();
return true;
}
catch (Exception ex)
{
}
return false;
}
3.4 删除文件夹
和创建文件一样,删除文件夹也包含,一级文件夹和多级文件夹,针对多级文件删除,需要从最内层向外逐级删除,
特别提醒 : 删除的文件夹是指空文件夹,多级文件夹,除了最内层每层除了包含待删除的不能包含其他的文件(夹),确保服务器安全,ftp就是这种机制
如上图,ftp服务器所指定的物理路径为:D:\ServerFtpRoot,资源上传,下载,删除,改名,以及文件夹创建删除等操作,都是在这个目录下进行的, 多级文件夹路径:/1/2/3/4/5,下面删除操作结果:
- 删除5: 5为空目录则删除成功,否则删除失败
- 删除4/5: 先操作第一步,5删除成功后,4为空目录则删除成功,否则删除失败
- 删除3/4/5: 先执行前两步,判断3是否为空文件夹,空文件夹才能删除,否则删除失败
- 删除2/3/4: 无法删除,是因为4为非空文件夹,还有5,所以2/3/4是不能删除的
- 删除2/3/4/5: 在4的文件夹下还有一个6(为文件或文件夹),执行结果: 成功删除了5,2/3/4是无法删除的
- 删除1/2/3/4/5: 若1,2,3,4,5都为空文件夹,执行后,1/2/3/4/5都会被删除
/// <summary>
/// 删除文件夹
/// </summary>
/// <param name="url"></param>
/// <param name="delDir">文件夹</param>
/// <param name="removeDir">删除子文件夹,为了安全禁止删除文件夹时删除文件以及其他子目录</param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static bool RemoveDirectory(string url, string delDir, bool removeDir = false, string userName = "", string password = "")
{
//删除远端path路径的文件夹
bool remove = _RemoveDirectory(url, delDir, userName, password);
if (!removeDir || !remove)
{
return remove;
}
//当path为多级文件夹时,需要依次删除
string[] dirs = delDir.Replace("\\", "/").Split('/');
int length = dirs.Length;
if (length <= 1)
{
return false;
}
for (int i = length - 2; i >= 0; i--)
{
if (string.IsNullOrEmpty(dirs[i]))
{
continue;
}
delDir = delDir.Substring(0, delDir.Length - 1 - dirs[i].Length);
remove = _RemoveDirectory(url, delDir, userName, password);
if (!remove)
{
return false;
}
}
return true;
}
上面是处理多级文件夹,下面是ftp删除实现
/// <summary>
/// 传出文件夹
/// </summary>
/// <param name="url"></param>
/// <param name="delDir"></param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
private static bool _RemoveDirectory(string url, string delDir, string userName = "", string password = "")
{
try
{
url = HttpUtility.UrlPathEncode(url + "/" + delDir);
FtpWebRequest ftpWebRequest = (FtpWebRequest)FtpWebRequest.Create(url);
ftpWebRequest.Method = WebRequestMethods.Ftp.RemoveDirectory;
ftpWebRequest.KeepAlive = false;
ftpWebRequest.Credentials = new NetworkCredential(userName, password);
FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
Stream stream = ftpWebResponse.GetResponseStream();
stream.Close();
ftpWebResponse.Close();
return true;
}
catch (Exception e)
{
}
return false;
}
3.5 文件上传(支持断点续传)
资源上传,若指定了ftp相对存储位置,也就是remotePath参数,分析了是否为多级文件夹的处理
/// <summary>
/// 上传文件
/// </summary>
/// <param name="url">ftp地址</param>
/// <param name="localfile">本地文件名</param>
/// <param name="remotePath">远传的文件名</param>
/// <param name="overwrite">是否覆盖远端文件</param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <param name="callback">进度回调</param>
public static bool Upload(string url, string localfile, string remotePath = "", bool overwrite = true, string userName = "", string password = "", Action<long, long> callback = null)
{
try
{
string oldUrl = url;
//获取文件名
string fileName = Path.GetFileName(localfile);
if (!string.IsNullOrEmpty(remotePath))
{
//路径连接
fileName = Path.Combine(remotePath, fileName);
}
//编码Url
url = HttpUtility.UrlPathEncode(url + "/" + fileName);
//获取远端文件夹路径
string dir = Path.GetDirectoryName(fileName);
//进行检测远端文件夹是否存在
bool check = CheckFtp(oldUrl + "/" + dir);
if (!check)
{
//不存在进行创建
check = MaskDirectory(oldUrl, dir, userName, password);
if (!check)
{
callback?.Invoke(-1, -1);
return false;
}
}
//获取远端文件大小(支持断点续传)
long uploadSize = GetDownloadFileSize(url);
//本地文件大小
FileInfo fileInfo = new FileInfo(localfile);
long totalSize = fileInfo.Length;
//远端文件大小与本地文件大小对比
if (uploadSize >= totalSize)
{
if (overwrite)
{
//覆盖,重新上传
uploadSize = 0;
}
else
{
//不覆盖,停止上传
callback?.Invoke(totalSize, totalSize);
return true;
}
}
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(url);
request.Credentials = new NetworkCredential(userName, password);
if (uploadSize > 0)
{
//断点续传
request.Method = WebRequestMethods.Ftp.AppendFile;
}
else
{
//上传
request.Method = WebRequestMethods.Ftp.UploadFile;
}
request.KeepAlive = false;//
request.UseBinary = true; //
//需上传大小
request.ContentLength = fileInfo.Length;
//读写块大小 4096B = 4k
byte[] buffer = new byte[4096];
int bytesRead = 0;
FileStream fileStream = fileInfo.OpenRead();
Stream stream = request.GetRequestStream();
if (uploadSize > 0)
{
//断点续传,将本地文件字节流读取位置设置到上次上传位置
fileStream.Seek(uploadSize, SeekOrigin.Begin);
}
do
{
//读写,上传
bytesRead = fileStream.Read(buffer, 0, buffer.Length);
uploadSize += bytesRead;
stream.Write(buffer, 0, bytesRead);
callback?.Invoke(uploadSize, totalSize);
}
while (bytesRead != 0);
//上传完毕
callback?.Invoke(totalSize, totalSize);
//关闭字节流
stream.Close();
//关闭文件流
fileStream.Close();
//关闭链请求
return true;
}
catch (Exception ex)
{
callback?.Invoke(-1, -1);
}
return false;
}
3.6 文件下载(支持断点续下)
/// <summary>
/// 下载文件(支持断点续下)
/// </summary>
/// <param name="url"></param>
/// <param name="desFile">本地文件夹路径</param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static bool Download(string url, string desDir, bool overwrite = true, string userName = "", string password = "", Action<long, long> callback = null)
{
string fileName = Path.GetFileName(url);
if (string.IsNullOrEmpty(desDir))
{
//路径为空,设置文件名
desDir = fileName;
}
else
{
if (!Directory.Exists(desDir))
{
//文件夹不存在,创建新文件夹
Directory.CreateDirectory(desDir);
}
//路径连接
desDir = Path.Combine(desDir, fileName);
}
url = HttpUtility.UrlPathEncode(url);
//下载文件大小
long downloadFileSize = GetDownloadFileSize(url, userName, password);
if (downloadFileSize <= 0)
{
//下载文件异常
callback?.Invoke(-1, -1);
return false;
}
bool flag = false;
long curPosition = 0;
Stream stream = null;
FileStream fileStream = null;
//下载未完成之前存为临时文件(支持断点续下)
string tmpFile = fileName + ".temp";
try
{
//下载文件已存在
if (File.Exists(desDir))
{
var fileInfo = new FileInfo(desDir);
curPosition = fileInfo.Length;
if (curPosition >= downloadFileSize)
{
//覆盖进行删除重新下载
if(overwrite)
{
File.Delete(desDir);
}
else
{
callback?.Invoke(downloadFileSize, downloadFileSize);
return true;
}
}
}
}
catch { }
try
{
//临时文件存在
if (File.Exists(tmpFile))
{
fileStream = File.OpenWrite(tmpFile);
curPosition = fileStream.Length;
//远端文件大小与临时文件大小对比
if (curPosition >= downloadFileSize)
{
//不覆盖
if(!overwrite)
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
//重命名
File.Move(tmpFile, desDir);
callback?.Invoke(downloadFileSize, downloadFileSize);
return true;
}
curPosition = 0;
}
callback?.Invoke(curPosition, downloadFileSize);
//将本地文件字节流写入位置设置成上次下载位置
fileStream.Seek(curPosition, SeekOrigin.Current);
}
else
{
fileStream = new FileStream(tmpFile, FileMode.Create, FileAccess.Write);
curPosition = 0;
}
FtpWebRequest ftpWebRequest = (FtpWebRequest)FtpWebRequest.Create(url);
ftpWebRequest.Credentials = new NetworkCredential(userName, password);
ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
ftpWebRequest.UseBinary = true;
if (curPosition > 0)
{
//断点续下,将请求下载的字节流下载位置设置成上次下载位置
ftpWebRequest.ContentOffset = curPosition;
}
FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
stream = ftpWebResponse.GetResponseStream();
byte[] buffer = new byte[4096];
int bytesRead = 0;
do
{
//读取, 写入本地
bytesRead = stream.Read(buffer, 0, buffer.Length);
curPosition += bytesRead;
callback?.Invoke(curPosition, downloadFileSize);
fileStream.Write(buffer, 0, bytesRead);
}
while (bytesRead != 0);
//下载完成
callback?.Invoke(downloadFileSize, downloadFileSize);
ftpWebResponse.Close();
flag = true;
}
catch (Exception e)
{
callback?.Invoke(-1, -1);
}
finally
{
if (stream != null)
{
stream.Close();
stream.Dispose();
}
if (fileStream != null)
{
fileStream.Flush();
fileStream.Close();
fileStream.Dispose();
}
if (flag)
{
File.Move(tmpFile, desDir);
}
}
return flag;
}
3.7 文件删除
/// <summary>
/// 删除文件
/// </summary>
/// <param name="url"></param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static bool Delete(string url, string userName = "", string password = "")
{
try
{
url = HttpUtility.UrlPathEncode(url);
FtpWebRequest ftpWebRequest = (FtpWebRequest)FtpWebRequest.Create(url);
ftpWebRequest.Method = WebRequestMethods.Ftp.DeleteFile;
ftpWebRequest.KeepAlive = false;
ftpWebRequest.Credentials = new NetworkCredential(userName, password);
FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
Stream stream = ftpWebResponse.GetResponseStream();
stream.Close();
ftpWebResponse.Close();
return true;
}
catch (Exception ex)
{
}
return false;
}
3.8 重命名
/// <summary>
/// 重命名
/// </summary>
/// <param name="url"></param>
/// <param name="newName">新名称,涉及到多级文件夹(未实现)</param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
/// <returns></returns>
public static bool Rename(string url, string newName, string userName = "", string password = "")
{
try
{
url = HttpUtility.UrlPathEncode(url);
FtpWebRequest ftpWebRequest = (FtpWebRequest)FtpWebRequest.Create(url);
ftpWebRequest.Method = WebRequestMethods.Ftp.Rename;
ftpWebRequest.RenameTo = newName;
ftpWebRequest.KeepAlive = false;
ftpWebRequest.Credentials = new NetworkCredential(userName, password);
FtpWebResponse ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
Stream stream = ftpWebResponse.GetResponseStream();
stream.Close();
ftpWebResponse.Close();
return true;
}
catch (Exception ex)
{
}
return false;
}
3.9 移动文件
/// <summary>
/// 移动文件夹
/// </summary>
/// <param name="url"></param>
/// <param name="newName"></param>
/// <param name="userName">账号</param>
/// <param name="password">密码</param>
public static void MoveFile(string url, string newName, string userName = "", string password = "")
{
Rename(url, newName, userName, password);
}
上面为HttpFileUtil.cs和FtpFileUtil.cs两个文件的所有方法,除了重命名和移动未测试,其他都基本测过,都可使用,记录结束