服务端推荐使用FileZilla Server Interface,支持获取文件大小SIZE命令和APPE续传到文件结尾。
具体实现函数
STATUS ftpAPPE(char host, / name of server host */
char user, / user name for host login */
char passwd, / password for host login */
char dirname, / directory to ‘cd’ to before sending command 网络的文件夹名*/
char filename, / filename to send with command */
char in_file / 上传的本地文件名 */);
下面是实现的过程 c文件
//基础下载函数 测试使用 返回错误 说明下载失败
int download(char host, / name of server host */
char user, / user name for host login */
char passwd, / password for host login */
char dirname, / directory to ‘cd’ to before sending command 网络的文件夹名*/
char filename, / filename to send with command */
char in_file / 下载的本地文件名 */) {
printf(“FTP ftpXfer RETR start!\n”);
int ctrlSock;
int dataSock;
char buf[512];
//char buf1[513];
int nBytes;
int nlocalwrite;
//原始文件 打开或者创建 。
int pFile;
STATUS status;
// char *in_file = “host:/c/test.text”;
pFile = open(in_file, O_WRONLY | O_CREAT, 0644);
ftpLibInit(5000L);
if (ftpXfer(host, user, passwd, “”, “RETR %s”, dirname, filename,
&ctrlSock, &dataSock) == ERROR)
return (ERROR);
while ((nBytes = read(dataSock, buf, sizeof(buf))) > 0 && (nlocalwrite
= write(pFile, buf, nBytes)) > 0) {
// 如果本地写的小于网络,说明错误 例如没有空间 。
if (nlocalwrite < nBytes) {
close(dataSock);
close(pFile);
close(ctrlSock);
return (ERROR);
}
// memset(buf,0,sizeof(buf1));
//memcpy(buf1, buf, nBytes);
//printf("\r\n it is [%s]", buf);
}
close(dataSock);
close(pFile);
if (nBytes < 0 || nlocalwrite < 0) /* read error? */
status = ERROR;
if (ftpReplyGet(ctrlSock, TRUE) != FTP_COMPLETE)
status = ERROR;
if (ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) != FTP_COMPLETE)
status = ERROR;
close(ctrlSock);
return (status);
}
//基础上传函数 测试使用 返回错误 说明上传失败 可能上传了一部分 测试因为本地,速度很快。
int upload(char host, / name of server host */
char user, / user name for host login */
char passwd, / password for host login */
char dirname, / directory to ‘cd’ to before sending command 网络的文件夹名*/
char filename, / filename to send with command */
char in_file / 上传的本地文件名 */
) {
//上传 开始
printf(“FTP ftpXfer STOR start!\n”);
int ctrlSock = -1;
int dataSock = -1;
char buf[512];
//网络读写长度
int nBytes;
//本地写的长度 或者读
int nlocalread;
//原始文件 打开或者创建 读取。
int pFile;
STATUS status =OK;
// char *in_file = “host:/c/test.text”;
pFile = open(in_file, O_RDONLY, 0644);
if (pFile < 0) {
printf(“File error\n”);
return (ERROR);
}
ftpLibInit(5000L);
if (ftpXfer(host, user, passwd, “”, “STOR %s”, dirname, filename,
&ctrlSock, &dataSock) == ERROR)
return (ERROR);
while ((nlocalread = read(pFile, buf, sizeof(buf))) > 0 && (nBytes = write(
dataSock, buf, nlocalread)) > 0) {
//如果网络小于读取的文件,说明连接被服务器中止,则返回错误。
if (nBytes < nlocalread) {
close(dataSock);
close(pFile);
close(ctrlSock);
return (ERROR);
}
// memset(buf,0,sizeof(buf1));
//memcpy(buf1, buf, nBytes);
//printf("\r\n it is [%s]", buf);
}
close(dataSock);
close(pFile);
if (nBytes < 0 || nlocalread < 0) /* read error? */
status = ERROR;
if (ftpReplyGet(ctrlSock, TRUE) != FTP_COMPLETE)
status = ERROR;
if (ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) != FTP_COMPLETE)
status = ERROR;
close(ctrlSock);
return (status);
}
//对ftpXfer改写增加获取长度命令。然后再此基础结合f读写函数上进行上传
LOCAL BOOL ftpTransientFatal(UINT32 reply);//ftp结果判断
UINT32 ftplTransientMaxRetryCount = 1; /* Retry just once for now */
UINT32 ftplTransientRetryInterval = 0; /* Default with no delay */
FUNCPTR _func_ftpTransientFatal = ftpTransientFatal;
LOCAL BOOL ftpTransientFatal(UINT32 reply /* Three digit code defined in RFC #959 */
) {
switch (reply) {
case (421): /* Service not available */
case (450): /* File unavailable */
case (451): /* error in processing */
case (452): /* insufficient storage */
{
/* yes, these are actually non-recoverable replies */
return (TRUE);
break;
}
/* attempt to retry the last command */
default:
return (FALSE);
}
}
//char * 转 long 专用 先偏移4,结尾可能有回车不一样; 这个函数不通用
long funs2l(char *s) {
long a = 0;
char *p = s;
p = p + 4;
while (*p != ‘\r’) {
a = a * 10 + *p - ‘0’;
p++;
}
return a;
}
//续传准备函数 注意参数cmd APPE %s
STATUS ftpXferAPPE(char host, / name of server host */
char user, / user name for host login */
char passwd, / password for host login */
char acct, / account for host login */
char cmd, / command to send to host */
char dirname, / directory to ‘cd’ to before sending command */
char filename, / filename to send with command */
int pCtrlSock, / where to return control socket fd */
int pDataSock, / where to return data socket fd, */
/* (NULL == don’t open data connection) */
long *psfilelen)
{
//上传
long sfilelen; /* 服务器文件大小 */
int replycode; /* 服务器对于SIZE 命令的回应码 */
char replyString[128]; /* 服务器对于SIZE 命令的回应值 */
memset(replyString, 0, 128);
int replyStringLength = 128; /* 服务器对于SIZE 命令的回应的最大长度,128应该够了 */
FAST int ctrlSock = ERROR;
FAST int dataSock = ERROR;
char *cmdsize = “SIZE %s”;
UINT32 ftpReply = 0;
UINT32 retryCount = 0;
int cmdResult;
struct fd_set readFds; /* Used by select for PORT method */
int width; /* Used by select for PORT method */
BOOL dataSockPassive = TRUE;
if (((ctrlSock = ftpHookup(host)) == ERROR) || (ftpLogin(ctrlSock, user,
passwd, acct) != OK) || (ftpCommand(ctrlSock, "TYPE I", 0, 0, 0, 0,
0, 0) != FTP_COMPLETE) || ((dirname[0] != EOS) && (ftpCommand(
ctrlSock, "CWD %s", (int) dirname, 0, 0, 0, 0, 0) != FTP_COMPLETE))) {
//有错误才执行这里
/* Detected an error during command establishment */
close(ctrlSock);
return (ERROR);
}
//获取长度 有文件就是213 没有是550 其它返回错误。
replycode = ftpCommandEnhanced(ctrlSock, cmdsize, filename, 0, 0, 0, 0, 0,
replyString, replyStringLength);
if (replycode == 213)
sfilelen = funs2l(replyString);
else if (replycode == 550)
sfilelen = 0L;
else {
close(ctrlSock);
return (ERROR);
}
printf("server file length %d\n", sfilelen);
if (psfilelen != NULL)
*psfilelen = sfilelen;
/*
* This is a special when an FTP command does not need to establish a
* data connection.
*/
if (pDataSock == NULL) {
if (ftpCommand(ctrlSock, cmd, (int) filename, 0, 0, 0, 0, 0)
!= FTP_COMPLETE) {
close(ctrlSock);
return (ERROR);
}
}
/*
* At this point we are trying to establish the data socket.
* We will first try using the modern, client-initiated PASV command.
* If PASV fails, then we will fall back to the PORT command.
*/
/* Set up local data port and send the PORT command */
do {
if ((dataSock = ftpDataConnInitPassiveMode(ctrlSock)) != ERROR) {
dataSockPassive = TRUE; /* We do not need to listen() on the socket */
} else {
if ((dataSock = ftpDataConnInit(ctrlSock)) == ERROR) {
close(ctrlSock);
return (ERROR);
} else
dataSockPassive = FALSE; /* We will need to listen() on the socket */
}
/* Send the FTP command. */
cmdResult = ftpCommandEnhanced(ctrlSock, cmd, (int) filename, 0, 0, 0,
0, 0, NULL, 0);
if ((cmdResult / 100) != FTP_PRELIM) {
/*
* The command has failed. Close the data socket and decode the error.
*/
close(dataSock);
/* Check if something really bad happened: File not found, etc. */
if ((cmdResult / 100) == FTP_ERROR) {
close(ctrlSock);
return (ERROR);
}
if ((cmdResult / 100) == FTP_TRANSIENT && _func_ftpTransientFatal
!= NULL) {
if ((*_func_ftpTransientFatal)(cmdResult) == TRUE) {
close(ctrlSock);
errno = S_ftpLib_FATAL_TRANSIENT_RESPONSE;
return (ERROR);
}
}
if ((ftpReply = (cmdResult / 100)) == FTP_TRANSIENT) {
/*
* If the error was due to transient error (e.x. the data port
* was not available) retry the command
* ftplTransientMaxRetryCount times.
*/
if (retryCount < ftplTransientMaxRetryCount) {
++retryCount;
if (ftplTransientRetryInterval)
taskDelay(ftplTransientRetryInterval);
continue; /* try another port */
} else {
/* Too many retries, close socket and return failure */
close(ctrlSock);
errno = S_ftpLib_TRANSIENT_RETRY_LIMIT_EXCEEDED;
return (ERROR);
}
/* Exit for any other error */
close(ctrlSock);
return (ERROR);
}
}
if (dataSockPassive == FALSE) {
/* At this point do a select on the data & control socket */
FD_ZERO (&readFds);
FD_SET (ctrlSock, &readFds);
FD_SET (dataSock, &readFds);
width = (dataSock > ctrlSock) ? dataSock : ctrlSock;
width++;
if (select(width, &readFds, NULL, NULL, NULL) == ERROR) {
close(dataSock);
close(ctrlSock);
return (ERROR);
}
/* If the control socket is ready process it and take a decision,
* try again or return error. If the data socket is ready call
* ftpDataConnGet next.
*/
if (FD_ISSET (ctrlSock, &readFds) && !FD_ISSET (dataSock, &readFds)) {
close(dataSock);
if ((ftpReply = ftpReplyGet(ctrlSock, FALSE)) == FTP_TRANSIENT)
continue; /* Try another port */
/* Regardless of response close sockets */
(void) ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0);
close(ctrlSock);
return (ERROR);
}
} /* PORT method requires checking for data socket connection */
} while (ftpReply == FTP_TRANSIENT); /* Try again, we might need a different port */
/* If we used PASV mode, then the socket is ready for use */
if (!dataSockPassive) {
/*
* We used the PORT method to establish a connection.
* The data socket connection is configured. Wait for the FTP server to connect
* to us.
*/
if ((dataSock = ftpDataConnGet(dataSock)) == ERROR) {
close(ctrlSock);
return (ERROR);
}
}
/* Store the control and data sockets */
if (pCtrlSock != NULL)
*pCtrlSock = ctrlSock;
if (pDataSock != NULL)
*pDataSock = dataSock;
return (OK);
}
//续传 函数 ,优先被动模式,注意参数cmd APPE %s
STATUS ftpAPPE(
char host, / name of server host */
char user, / user name for host login */
char passwd, / password for host login */
char dirname, / directory to ‘cd’ to before sending command 网络的文件夹名*/
char filename, / filename to send with command */
char in_file / 上传的本地文件名 */)
{
//上传
printf(“FTP ftpXferAPPE start!\n”);
//char host = “192.168.200.254”; / name of server host */
//char user = “22422”; / user name for host login */
//char passwd = “22422”; / password for host login */
char acct = “”; / account for host login */
//char *in_file = “host:/c/cfg.ini”; //本地文件
// char cmdsize=”SIZE %s”; / command to send to host */
char *cmd = “APPE %s”;
//char dirname = “/”; / directory to ‘cd’ to before sending command */
//char filename = “test.txt”; / filename to send with command */
int ctrlSock = -1; /* where to return control socket fd */
int dataSock = -1; /* where to return data socket fd, // (NULL == don’t open data connection) */
long sfilelen = 0; /* 服务器文件大小 */
STATUS status = OK;
if (ftpXferAPPE(host, user, passwd, acct, cmd, dirname, filename,
&ctrlSock, &dataSock, &sfilelen) == ERROR)
return (ERROR);
//OK==0 TRUE==1
// int x= upload( host,user,passwd,dirname,filename,in_file);
//已经准备好了,APPE上传开始。 获得本地文件大小
FILE * pFile ;
pFile = fopen(in_file, “rb”);
if (pFile == NULL) {
fputs(“File open error”, stderr);
// 发送服务端错误码。
return ERROR;
}
fseek(pFile, 0, SEEK_END);
long lSize = ftell(pFile);
if (lSize <= sfilelen) {
printf(“upload aready OK no need upload %d,%d\n”, lSize,sfilelen);
close(dataSock);
close(pFile);
close(ctrlSock);
return (status);
}
//读取 上传
//网络读写长度
int nBytes;
//本地写的长度 或者读
int lBytes;
//复位 后移动也可以直接移动。当 读取个数为1时,返回值才是字节数
rewind(pFile);
fseek(pFile, sfilelen, SEEK_CUR);
char filebuffer[1024];
while ((lBytes = fread(filebuffer, 1, sizeof(filebuffer), pFile)) > 0
&& (nBytes = write(dataSock, filebuffer, lBytes)) > 0) {
//如果网络小于读取的文件,一般说明连接被服务器中止,
if (nBytes < lBytes) {
//移动指针回去lBytes,前进nBytes
fseek(pFile, -lBytes, SEEK_CUR);
fseek(pFile, nBytes, SEEK_CUR);
}
}
if (ferror ( pFile )) {
printf("File read error.");
}
close(dataSock);
close(pFile);
if (nBytes < 0 || lBytes < 0) /* read error? */
status = ERROR;
if (ftpReplyGet(ctrlSock, TRUE) != FTP_COMPLETE)
status = ERROR;
if (ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) != FTP_COMPLETE)
status = ERROR;
printf("upload aready OK!\n");
close(ctrlSock);
return (status);
}