文档说明
本文档使用socket通信方式来实现ftp文件的上传下载等命令的执行
1.基本介绍
由于最近的项目是客户端的程序,需要将客户端的图片文件【切图】-【打包】-【ftp上传】,现在就差最后一步了,慢慢的把这些小功能实现了,合并到一起就是一个大功能了,所以一个业务需要拆分的很小很小才可以看清楚,这个项目实际需要用到哪些知识点,下面介绍一下ftp上传的命令
ftp命令的参考链接:
ftp适合小文件上传
对带宽要求要求较高
服务器安全性也要考虑到
命令需要熟悉,不然比较难
2.实际项目
文件上传
文件下载
删除文件
创建文件夹
文件夹重命名
删除文件夹
改变目录
获取文件夹中文件列表
等等
2.1 图片上传和下载
写了几个方法,一般用的最多的就是put,具体的可以下载复制源码下来进行实战一下。
2.2 目录创建和删除
这个方法今天刚好用上了,折腾了一会,才搞定的。
3.调用代码参考
由于这个帮助类不是静态的,所以需要实例化
string username = "xxx" ;
string password = "xxx" ;
var ftp = new ftpclienthelper( "xxx" , "." , username, password, 1021);
下面还是调用常用的方法,就可以了,因为账号、密码、服务器的ip地址都被我用[xxx]代替了,所以大家自己改下,还有ftp默认端口号是:1021,如果有变动还是需要自己改下的。
4.ftpclienthelper下载
//-------------------------------------------------------------------------------------
// all rights reserved , copyright (c) 2015 , zto , ltd .
//-------------------------------------------------------------------------------------
using system;
using system.io;
using system.net;
using system.net.sockets;
using system.text;
using system.threading;
namespace zto.pictest.utilities
{
/// <summary>
/// ftp操作帮助类
///
/// 修改纪录
///
/// 2016-4-4 版本:1.0 yanghenglian 创建主键,注意命名空间的排序,测试非常好。
///
/// 版本:1.0
///
/// <author>
/// <name>yanghenglian</name>
/// <date>2016-4-4</date>
/// </author>
/// </summary>
public class ftpclienthelper
{
public static object obj = new object ();
#region 构造函数
/// <summary>
/// 缺省构造函数
/// </summary>
public ftpclienthelper()
{
remotehost = "" ;
_strremotepath = "" ;
_strremoteuser = "" ;
_strremotepass = "" ;
_strremoteport = 21;
_bconnected = false ;
}
/// <summary>
/// 构造函数
/// </summary>
public ftpclienthelper( string remotehost, string remotepath, string remoteuser, string remotepass, int remoteport)
{
// ip地址
remotehost = remotehost;
// 这个很重要,表示连接路径,如果是.表示根目录
_strremotepath = remotepath;
// 登录账号
_strremoteuser = remoteuser;
// 登录密码
_strremotepass = remotepass;
// ftp端口号
_strremoteport = remoteport;
connect();
}
#endregion
#region 字段
private int _strremoteport;
private boolean _bconnected;
private string _strremotepass;
private string _strremoteuser;
private string _strremotepath;
/// <summary>
/// 服务器返回的应答信息(包含应答码)
/// </summary>
private string _strmsg;
/// <summary>
/// 服务器返回的应答信息(包含应答码)
/// </summary>
private string _strreply;
/// <summary>
/// 服务器返回的应答码
/// </summary>
private int _ireplycode;
/// <summary>
/// 进行控制连接的socket
/// </summary>
private socket _socketcontrol;
/// <summary>
/// 传输模式
/// </summary>
private transfertype _trtype;
/// <summary>
/// 接收和发送数据的缓冲区
/// </summary>
private const int blocksize = 512;
/// <summary>
/// 编码方式
/// </summary>
readonly encoding _ascii = encoding.ascii;
/// <summary>
/// 字节数组
/// </summary>
readonly byte [] _buffer = new byte [blocksize];
#endregion
#region 属性
/// <summary>
/// ftp服务器ip地址
/// </summary>
public string remotehost { get ; set ; }
/// <summary>
/// ftp服务器端口
/// </summary>
public int remoteport
{
get
{
return _strremoteport;
}
set
{
_strremoteport = value;
}
}
/// <summary>
/// 当前服务器目录
/// </summary>
public string remotepath
{
get
{
return _strremotepath;
}
set
{
_strremotepath = value;
}
}
/// <summary>
/// 登录用户账号
/// </summary>
public string remoteuser
{
set
{
_strremoteuser = value;
}
}
/// <summary>
/// 用户登录密码
/// </summary>
public string remotepass
{
set
{
_strremotepass = value;
}
}
/// <summary>
/// 是否登录
/// </summary>
public bool connected
{
get
{
return _bconnected;
}
}
#endregion
#region 链接
/// <summary>
/// 建立连接
/// </summary>
public void connect()
{
lock (obj)
{
_socketcontrol = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
var ep = new ipendpoint(ipaddress.parse(remotehost), _strremoteport);
try
{
_socketcontrol.connect(ep);
}
catch (exception)
{
throw new ioexception( "不能连接ftp服务器" );
}
}
readreply();
if (_ireplycode != 220)
{
disconnect();
throw new ioexception(_strreply.substring(4));
}
sendcommand( "user " + _strremoteuser);
if (!(_ireplycode == 331 || _ireplycode == 230))
{
closesocketconnect();
throw new ioexception(_strreply.substring(4));
}
if (_ireplycode != 230)
{
sendcommand( "pass " + _strremotepass);
if (!(_ireplycode == 230 || _ireplycode == 202))
{
closesocketconnect();
throw new ioexception(_strreply.substring(4));
}
}
_bconnected = true ;
chdir(_strremotepath);
}
/// <summary>
/// 关闭连接
/// </summary>
public void disconnect()
{
if (_socketcontrol != null )
{
sendcommand( "quit" );
}
closesocketconnect();
}
#endregion
#region 传输模式
/// <summary>
/// 传输模式:二进制类型、ascii类型
/// </summary>
public enum transfertype { binary, ascii };
/// <summary>
/// 设置传输模式
/// </summary>
/// <param name="tttype">传输模式</param>
public void settransfertype(transfertype tttype)
{
sendcommand(tttype == transfertype.binary ? "type i" : "type a" );
if (_ireplycode != 200)
{
throw new ioexception(_strreply.substring(4));
}
_trtype = tttype;
}
/// <summary>
/// 获得传输模式
/// </summary>
/// <returns>传输模式</returns>
public transfertype gettransfertype()
{
return _trtype;
}
#endregion
#region 文件操作
/// <summary>
/// 获得文件列表
/// </summary>
/// <param name="strmask">文件名的匹配字符串</param>
public string [] dir( string strmask)
{
if (!_bconnected)
{
connect();
}
socket socketdata = createdatasocket();
sendcommand( "nlst " + strmask);
if (!(_ireplycode == 150 || _ireplycode == 125 || _ireplycode == 226))
{
throw new ioexception(_strreply.substring(4));
}
_strmsg = "" ;
thread.sleep(2000);
while ( true )
{
int ibytes = socketdata.receive(_buffer, _buffer.length, 0);
_strmsg += _ascii.getstring(_buffer, 0, ibytes);
if (ibytes < _buffer.length)
{
break ;
}
}
char [] seperator = { '\n' };
string [] strsfilelist = _strmsg.split(seperator);
socketdata.close(); //数据socket关闭时也会有返回码
if (_ireplycode != 226)
{
readreply();
if (_ireplycode != 226)
{
throw new ioexception(_strreply.substring(4));
}
}
return strsfilelist;
}
public void newputbyguid( string strfilename, string strguid)
{
if (!_bconnected)
{
connect();
}
string str = strfilename.substring(0, strfilename.lastindexof( "\\" , stringcomparison.ordinal));
string strtypename = strfilename.substring(strfilename.lastindexof( "." , stringcomparison.ordinal));
strguid = str + "\\" + strguid;
socket socketdata = createdatasocket();
sendcommand( "stor " + path.getfilename(strguid));
if (!(_ireplycode == 125 || _ireplycode == 150))
{
throw new ioexception(_strreply.substring(4));
}
var input = new filestream(strguid, filemode.open);
input.flush();
int ibytes;
while ((ibytes = input.read(_buffer, 0, _buffer.length)) > 0)
{
socketdata.send(_buffer, ibytes, 0);
}
input.close();
if (socketdata.connected)
{
socketdata.close();
}
if (!(_ireplycode == 226 || _ireplycode == 250))
{
readreply();
if (!(_ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
}
}
/// <summary>
/// 获取文件大小
/// </summary>
/// <param name="strfilename">文件名</param>
/// <returns>文件大小</returns>
public long getfilesize( string strfilename)
{
if (!_bconnected)
{
connect();
}
sendcommand( "size " + path.getfilename(strfilename));
long lsize;
if (_ireplycode == 213)
{
lsize = int64.parse(_strreply.substring(4));
}
else
{
throw new ioexception(_strreply.substring(4));
}
return lsize;
}
/// <summary>
/// 获取文件信息
/// </summary>
/// <param name="strfilename">文件名</param>
/// <returns>文件大小</returns>
public string getfileinfo( string strfilename)
{
if (!_bconnected)
{
connect();
}
socket socketdata = createdatasocket();
sendcommand( "list " + strfilename);
if (!(_ireplycode == 150 || _ireplycode == 125
|| _ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
byte [] b = new byte [512];
memorystream ms = new memorystream();
while ( true )
{
int ibytes = socketdata.receive(b, b.length, 0);
ms.write(b, 0, ibytes);
if (ibytes <= 0)
{
break ;
}
}
byte [] bt = ms.getbuffer();
string strresult = encoding.ascii.getstring(bt);
ms.close();
return strresult;
}
/// <summary>
/// 删除
/// </summary>
/// <param name="strfilename">待删除文件名</param>
public void delete( string strfilename)
{
if (!_bconnected)
{
connect();
}
sendcommand( "dele " + strfilename);
if (_ireplycode != 250)
{
throw new ioexception(_strreply.substring(4));
}
}
/// <summary>
/// 重命名(如果新文件名与已有文件重名,将覆盖已有文件)
/// </summary>
/// <param name="stroldfilename">旧文件名</param>
/// <param name="strnewfilename">新文件名</param>
public void rename( string stroldfilename, string strnewfilename)
{
if (!_bconnected)
{
connect();
}
sendcommand( "rnfr " + stroldfilename);
if (_ireplycode != 350)
{
throw new ioexception(_strreply.substring(4));
}
// 如果新文件名与原有文件重名,将覆盖原有文件
sendcommand( "rnto " + strnewfilename);
if (_ireplycode != 250)
{
throw new ioexception(_strreply.substring(4));
}
}
#endregion
#region 上传和下载
/// <summary>
/// 下载一批文件
/// </summary>
/// <param name="strfilenamemask">文件名的匹配字符串</param>
/// <param name="strfolder">本地目录(不得以\结束)</param>
public void get ( string strfilenamemask, string strfolder)
{
if (!_bconnected)
{
connect();
}
string [] strfiles = dir(strfilenamemask);
foreach ( string strfile in strfiles)
{
if (!strfile.equals( "" )) //一般来说strfiles的最后一个元素可能是空字符串
{
get (strfile, strfolder, strfile);
}
}
}
/// <summary>
/// 下载一个文件
/// </summary>
/// <param name="strremotefilename">要下载的文件名</param>
/// <param name="strfolder">本地目录(不得以\结束)</param>
/// <param name="strlocalfilename">保存在本地时的文件名</param>
public void get ( string strremotefilename, string strfolder, string strlocalfilename)
{
socket socketdata = createdatasocket();
try
{
if (!_bconnected)
{
connect();
}
settransfertype(transfertype.binary);
if (strlocalfilename.equals( "" ))
{
strlocalfilename = strremotefilename;
}
sendcommand( "retr " + strremotefilename);
if (!(_ireplycode == 150 || _ireplycode == 125 || _ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
var output = new filestream(strfolder + "\\" + strlocalfilename, filemode.create);
while ( true )
{
int ibytes = socketdata.receive(_buffer, _buffer.length, 0);
output.write(_buffer, 0, ibytes);
if (ibytes <= 0)
{
break ;
}
}
output.close();
if (socketdata.connected)
{
socketdata.close();
}
if (!(_ireplycode == 226 || _ireplycode == 250))
{
readreply();
if (!(_ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
}
}
catch
{
socketdata.close();
_socketcontrol.close();
_bconnected = false ;
_socketcontrol = null ;
}
}
/// <summary>
/// 下载一个文件
/// </summary>
/// <param name="strremotefilename">要下载的文件名</param>
/// <param name="strfolder">本地目录(不得以\结束)</param>
/// <param name="strlocalfilename">保存在本地时的文件名</param>
public void getnobinary( string strremotefilename, string strfolder, string strlocalfilename)
{
if (!_bconnected)
{
connect();
}
if (strlocalfilename.equals( "" ))
{
strlocalfilename = strremotefilename;
}
socket socketdata = createdatasocket();
sendcommand( "retr " + strremotefilename);
if (!(_ireplycode == 150 || _ireplycode == 125 || _ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
var output = new filestream(strfolder + "\\" + strlocalfilename, filemode.create);
while ( true )
{
int ibytes = socketdata.receive(_buffer, _buffer.length, 0);
output.write(_buffer, 0, ibytes);
if (ibytes <= 0)
{
break ;
}
}
output.close();
if (socketdata.connected)
{
socketdata.close();
}
if (!(_ireplycode == 226 || _ireplycode == 250))
{
readreply();
if (!(_ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
}
}
/// <summary>
/// 上传一批文件
/// </summary>
/// <param name="strfolder">本地目录(不得以\结束)</param>
/// <param name="strfilenamemask">文件名匹配字符(可以包含*和?)</param>
public void put( string strfolder, string strfilenamemask)
{
string [] strfiles = directory.getfiles(strfolder, strfilenamemask);
foreach ( string strfile in strfiles)
{
put(strfile);
}
}
/// <summary>
/// 上传一个文件
/// </summary>
/// <param name="strfilename">本地文件名</param>
public void put( string strfilename)
{
if (!_bconnected)
{
connect();
}
socket socketdata = createdatasocket();
if (path.getextension(strfilename) == "" )
sendcommand( "stor " + path.getfilenamewithoutextension(strfilename));
else
sendcommand( "stor " + path.getfilename(strfilename));
if (!(_ireplycode == 125 || _ireplycode == 150))
{
throw new ioexception(_strreply.substring(4));
}
var input = new filestream(strfilename, filemode.open);
int ibytes;
while ((ibytes = input.read(_buffer, 0, _buffer.length)) > 0)
{
socketdata.send(_buffer, ibytes, 0);
}
input.close();
if (socketdata.connected)
{
socketdata.close();
}
if (!(_ireplycode == 226 || _ireplycode == 250))
{
readreply();
if (!(_ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
}
}
/// <summary>
/// 上传一个文件
/// </summary>
/// <param name="strfilename">本地文件名</param>
///
/// <param name="strguid"> </param>
public void putbyguid( string strfilename, string strguid)
{
if (!_bconnected)
{
connect();
}
string str = strfilename.substring(0, strfilename.lastindexof( "\\" , stringcomparison.ordinal));
string strtypename = strfilename.substring(strfilename.lastindexof( "." , system.stringcomparison.ordinal));
strguid = str + "\\" + strguid;
file.copy(strfilename, strguid);
file.setattributes(strguid, fileattributes.normal);
socket socketdata = createdatasocket();
sendcommand( "stor " + path.getfilename(strguid));
if (!(_ireplycode == 125 || _ireplycode == 150))
{
throw new ioexception(_strreply.substring(4));
}
var input = new filestream(strguid, filemode.open, system.io.fileaccess.read, system.io.fileshare.read);
int ibytes = 0;
while ((ibytes = input.read(_buffer, 0, _buffer.length)) > 0)
{
socketdata.send(_buffer, ibytes, 0);
}
input.close();
file.delete(strguid);
if (socketdata.connected)
{
socketdata.close();
}
if (!(_ireplycode == 226 || _ireplycode == 250))
{
readreply();
if (!(_ireplycode == 226 || _ireplycode == 250))
{
throw new ioexception(_strreply.substring(4));
}
}
}
#endregion
#region 目录操作
/// <summary>
/// 创建目录
/// </summary>
/// <param name="strdirname">目录名</param>
public void mkdir( string strdirname)
{
if (!_bconnected)
{
connect();
}
sendcommand( "mkd " + strdirname);
if (_ireplycode != 257)
{
throw new ioexception(_strreply.substring(4));
}
}
/// <summary>
/// 删除目录
/// </summary>
/// <param name="strdirname">目录名</param>
public void rmdir( string strdirname)
{
if (!_bconnected)
{
connect();
}
sendcommand( "rmd " + strdirname);
if (_ireplycode != 250)
{
throw new ioexception(_strreply.substring(4));
}
}
/// <summary>
/// 改变目录
/// </summary>
/// <param name="strdirname">新的工作目录名</param>
public void chdir( string strdirname)
{
if (strdirname.equals( "." ) || strdirname.equals( "" ))
{
return ;
}
if (!_bconnected)
{
connect();
}
sendcommand( "cwd " + strdirname);
if (_ireplycode != 250)
{
throw new ioexception(_strreply.substring(4));
}
this ._strremotepath = strdirname;
}
#endregion
#region 内部函数
/// <summary>
/// 将一行应答字符串记录在strreply和strmsg,应答码记录在ireplycode
/// </summary>
private void readreply()
{
_strmsg = "" ;
_strreply = readline();
_ireplycode = int32.parse(_strreply.substring(0, 3));
}
/// <summary>
/// 建立进行数据连接的socket
/// </summary>
/// <returns>数据连接socket</returns>
private socket createdatasocket()
{
sendcommand( "pasv" );
if (_ireplycode != 227)
{
throw new ioexception(_strreply.substring(4));
}
int index1 = _strreply.indexof( '(' );
int index2 = _strreply.indexof( ')' );
string ipdata = _strreply.substring(index1 + 1, index2 - index1 - 1);
int [] parts = new int [6];
int len = ipdata.length;
int partcount = 0;
string buf = "" ;
for ( int i = 0; i < len && partcount <= 6; i++)
{
char ch = char .parse(ipdata.substring(i, 1));
if ( char .isdigit(ch))
buf += ch;
else if (ch != ',' )
{
throw new ioexception( "malformed pasv strreply: " + _strreply);
}
if (ch == ',' || i + 1 == len)
{
try
{
parts[partcount++] = int32.parse(buf);
buf = "" ;
}
catch (exception)
{
throw new ioexception( "malformed pasv strreply: " + _strreply);
}
}
}
string ipaddress = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];
int port = (parts[4] << 8) + parts[5];
var s = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
var ep = new ipendpoint(ipaddress.parse(ipaddress), port);
try
{
s.connect(ep);
}
catch (exception)
{
throw new ioexception( "无法连接ftp服务器" );
}
return s;
}
/// <summary>
/// 关闭socket连接(用于登录以前)
/// </summary>
private void closesocketconnect()
{
lock (obj)
{
if (_socketcontrol != null )
{
_socketcontrol.close();
_socketcontrol = null ;
}
_bconnected = false ;
}
}
/// <summary>
/// 读取socket返回的所有字符串
/// </summary>
/// <returns>包含应答码的字符串行</returns>
private string readline()
{
lock (obj)
{
while ( true )
{
int ibytes = _socketcontrol.receive(_buffer, _buffer.length, 0);
_strmsg += _ascii.getstring(_buffer, 0, ibytes);
if (ibytes < _buffer.length)
{
break ;
}
}
}
char [] seperator = { '\n' };
string [] mess = _strmsg.split(seperator);
if (_strmsg.length > 2)
{
_strmsg = mess[mess.length - 2];
}
else
{
_strmsg = mess[0];
}
if (!_strmsg.substring(3, 1).equals( " " )) //返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串)
{
return readline();
}
return _strmsg;
}
/// <summary>
/// 发送命令并获取应答码和最后一行应答字符串
/// </summary>
/// <param name="strcommand">命令</param>
public void sendcommand( string strcommand)
{
lock (obj)
{
byte [] cmdbytes = encoding.ascii.getbytes((strcommand + "\r\n" ).tochararray());
_socketcontrol.send(cmdbytes, cmdbytes.length, 0);
thread.sleep(100);
readreply();
}
}
#endregion
}
}
5.ftp常用的命令
#region 程序集 system.dll, v4.0.0.0
// c:\program files (x86)\reference assemblies\microsoft\framework\.netframework\v4.0\system.dll
#endregion
using system;
namespace system.net
{
// 摘要:
// system.net.webrequestmethods.ftp、system.net.webrequestmethods.file 和 system.net.webrequestmethods.http
// 类的容器类。无法继承此类
public static class webrequestmethods
{
// 摘要:
// 表示可用于 file 请求的文件协议方法的类型。无法继承此类。
public static class file
{
// 摘要:
// 表示用来从指定的位置检索文件的 file get 协议方法。
public const string downloadfile = "get" ;
//
// 摘要:
// 表示用来将文件复制到指定位置的 file put 协议方法。
public const string uploadfile = "put" ;
}
// 摘要:
// 表示可与 ftp 请求一起使用的 ftp 协议方法的类型。无法继承此类。
public static class ftp
{
// 摘要:
// 表示要用于将文件追加到 ftp 服务器上的现有文件的 ftp appe 协议方法。
public const string appendfile = "appe" ;
//
// 摘要:
// 表示要用于删除 ftp 服务器上的文件的 ftp dele 协议方法。
public const string deletefile = "dele" ;
//
// 摘要:
// 表示要用于从 ftp 服务器下载文件的 ftp retr 协议方法。
public const string downloadfile = "retr" ;
//
// 摘要:
// 表示要用于从 ftp 服务器上的文件检索日期时间戳的 ftp mdtm 协议方法。
public const string getdatetimestamp = "mdtm" ;
//
// 摘要:
// 表示要用于检索 ftp 服务器上的文件大小的 ftp size 协议方法。
public const string getfilesize = "size" ;
//
// 摘要:
// 表示获取 ftp 服务器上的文件的简短列表的 ftp nlist 协议方法。
public const string listdirectory = "nlst" ;
//
// 摘要:
// 表示获取 ftp 服务器上的文件的详细列表的 ftp list 协议方法。
public const string listdirectorydetails = "list" ;
//
// 摘要:
// 表示在 ftp 服务器上创建目录的 ftp mkd 协议方法。
public const string makedirectory = "mkd" ;
//
// 摘要:
// 表示打印当前工作目录的名称的 ftp pwd 协议方法。
public const string printworkingdirectory = "pwd" ;
//
// 摘要:
// 表示移除目录的 ftp rmd 协议方法。
public const string removedirectory = "rmd" ;
//
// 摘要:
// 表示重命名目录的 ftp rename 协议方法。
public const string rename = "rename" ;
//
// 摘要:
// 表示将文件上载到 ftp 服务器的 ftp stor 协议方法。
public const string uploadfile = "stor" ;
//
// 摘要:
// 表示将具有唯一名称的文件上载到 ftp 服务器的 ftp stou 协议方法。
public const string uploadfilewithuniquename = "stou" ;
}
// 摘要:
// 表示可与 http 请求一起使用的 http 协议方法的类型。
public static class http
{
// 摘要:
// 表示与代理一起使用的 http connect 协议方法,该代理可以动态切换到隧道,如 ssl 隧道的情况。
public const string connect = "connect" ;
//
// 摘要:
// 表示一个 http get 协议方法。
public const string get = "get" ;
//
// 摘要:
// 表示一个 http head 协议方法。除了服务器在响应中只返回消息头不返回消息体以外,head 方法和 get 是一样的。
public const string head = "head" ;
//
// 摘要:
// 表示一个 http mkcol 请求,该请求在请求 uri(统一资源标识符)指定的位置新建集合,如页的集合。
public const string mkcol = "mkcol" ;
//
// 摘要:
// 表示一个 http post 协议方法,该方法用于将新实体作为补充发送到某个 uri。
public const string post = "post" ;
//
// 摘要:
// 表示一个 http put 协议方法,该方法用于替换 uri 标识的实体。
public const string put = "put" ;
}
}
}
以上就是本文的全部内容,希望能给大家一个参考,也希望大家多多支持服务器之家。
dy("nrwz");
查看更多关于FTPClientHelper辅助类 实现文件上传,目录操作,下载等操作的详细内容...