linux C++上传FTP,亲测有效童叟无欺

说明:首先包含下面的头文件至项目中,头文件中只提供了上传单个文件到指定ftp目录的功能,完整的demo在最下方,如果有用的话,记得回来点赞

测试环境: ubuntu 18.04,vsftpd 请大家检查是否打开了允许创建文件夹的权限

#ifndef FTPTOOL_H
#define FTPTOOL_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<cctype>
#include <sys/types.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include<iostream>
#include<dirent.h>
#include<string>

#define MAX_LEN 1024*1
#define MAX_CMD_LEN 129

static inline int ftp_socket_send(int fd, char *str)
{
    send(fd, str, strlen(str), 0);
    return 0;
}

static inline int ftp_socket_recv(int fd, char *str)
{
    int size;
    size = recv(fd, str, MAX_LEN-1, 0);
    str[size] = 0;

    //printf("ftp recv: %s\n",str);
    return 0;
}

static int ftp_get_data_port(char *buff, in_port_t *port)
{
    int i = 0, j = 0;
    short port_l = 0, port_h = 0;

    if (buff == NULL || port == NULL)
    {
        return -1;
    }
    // (192,168,186,1,4,0).
    while (buff[i++] != '(');
    while (j < 4)
    {
        if(buff[i++] == ',')
            j++;
    }

    while (buff[i] != ',')
    {
        port_h *= 10;
        port_h += buff[i] - 0x30;
        i++;
    }

    i++;
    while (buff[i] != ')')
    {
        port_l *= 10;
        port_l += buff[i] - 0x30;
        i++;
    }

    printf("data_port : %u\n", port_h << 8 | port_l);
    *port = htons((short)(port_h << 8 | port_l));

    return 0;
}
static int ftp_get_upload_file_name(const char *upload_file, char *file_name)
{
    int i = 0;
    int path_lenth = 0;

    if (upload_file == NULL || file_name == NULL)
    {
        return -1;
    }

    path_lenth = strlen(upload_file);

    while (path_lenth - i > 0)
    {
        // find index of '/'
        if (upload_file[path_lenth - i]== 47)
        {
            i--;
            break;
        }
        i++;
    }

    strcpy(file_name, &upload_file[path_lenth - i]);


    return 0;
}

int ftp_upload_file(const char *ip, unsigned int port, const char *user, const char *pwd,
const char *upload_file,const char *upload_name, const char *folder_name)
{
    int ret;
    int size;
    char buff[MAX_LEN];
    char cmd[MAX_CMD_LEN];
    char file_name[256];
    int fd_socket, fd_data;
    struct sockaddr_in addr;
    struct sockaddr_in data;
    int send_ret=0;

    addr.sin_family = AF_INET;
    inet_aton(ip, &addr.sin_addr);
    addr.sin_port   = htons(port);
    data.sin_family = AF_INET;
    inet_aton(ip, &data.sin_addr);
    //用于建立和ftp服务的连接,通过21端口
    fd_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (fd_socket == -1) {
        return -1;
    }
    fd_data = socket(AF_INET, SOCK_STREAM, 0);
    if (fd_data == -1) {
        close(fd_socket);
        return -1;
    }
    ret = connect(fd_socket, (struct sockaddr *)&addr, sizeof(addr));
    if (ret != 0) {
        close(fd_data);
        close(fd_socket);
        printf("connet falied\n");
        return -1;
    }

    size = recv(fd_socket, buff, MAX_LEN-1, 0);
    buff[size] = 0;
    //向ftp服务器发送登录使用的用户名
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "USER %s\r\n", user);
    // ftp_socket_send(fd_socket, "PASS shikejun\r\n");
    ftp_socket_send(fd_socket, cmd);
    ftp_socket_recv(fd_socket, buff);
    //向ftp服务器发送登录使用的密码
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "PASS %s\r\n", pwd);
    ftp_socket_send(fd_socket, cmd);
    ftp_socket_recv(fd_socket, buff);
    //返回服务器使用的操作系统
    ftp_socket_send(fd_socket, "SYST\r\n");
    ftp_socket_recv(fd_socket, buff);
    //指定数据类型,A=ASCII, E=EBCDIC, I=binary
    ftp_socket_send(fd_socket, "TYPE I\r\n");
    ftp_socket_recv(fd_socket, buff);
    //用被动方式向ftp请求连接,ftp返回一个端口,用于上传文件
    ftp_socket_send(fd_socket, "PASV\r\n");
    ftp_socket_recv(fd_socket, buff);

    ftp_get_data_port(buff, &data.sin_port);

    memset(file_name, 0, sizeof(file_name));
    if(upload_name==NULL||strlen(upload_name)<=1) {
        ftp_get_upload_file_name(upload_file, file_name);
    }
    else {
        strcpy(file_name,upload_name);
    }

    //进入ftp中的folder_name
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "CWD %s\r\n", folder_name);
    ftp_socket_send(fd_socket, cmd);
    ftp_socket_recv(fd_socket, buff);
	
    char *cmp_res = "550 Failed to change directory.";
    char buff_res[31];
    for (int i=0; i<31; i++) {
        buff_res[i] = buff[i];
    }
    //如果ftp中目前没有folder_name,则会返回一个550 Failed to change directory.
    if (!strcmp(buff_res, cmp_res)) {
        //如果不存在folder_name,则首先进行创建,然后在改变在ftp中工作目录为folder_name
        memset(cmd, 0, sizeof(cmd));
        sprintf(cmd, "MKD %s\r\n", folder_name);
        ftp_socket_send(fd_socket, cmd);
        ftp_socket_recv(fd_socket, buff);

        memset(cmd, 0, sizeof(cmd));
        sprintf(cmd, "CWD %s\r\n", folder_name);
        ftp_socket_send(fd_socket, cmd);
        ftp_socket_recv(fd_socket, buff);
    }
    //在ftp中建立文件,名为file_name
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "STOR %s\r\n", file_name);
    ftp_socket_send(fd_socket, cmd);
    //通过fd_data进行文件上传
    ret = connect(fd_data, (struct sockaddr *)&data, sizeof(data));
    if (ret != 0) {
        printf("connet falied\n");
        close(fd_data);
        close(fd_socket);
        return -1;
    }

    ftp_socket_recv(fd_socket, buff);
    //打开本地文件
    int fd = open(upload_file, O_RDONLY);
    if (fd == -1) {
        printf("open: \n");
        close(fd_data);
        close(fd_socket);
        return -1;
    }
    //进行流的操作,read读取本地文件数据后再send到ftp中
    while ((ret = read(fd, buff, sizeof(buff))) > 0) {
        send_ret = send(fd_data, buff, ret, 0);
        if(send_ret<=0) {
            break;
        }
        //usleep(30*1000);
    }
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "QUIT\r\n");
    ftp_socket_send(fd_socket, cmd);
    close(fd);
    close(fd_data);

    ftp_socket_recv(fd_socket, buff);

    close(fd_socket);

    printf("ftp_upload [%s] [%s] result: %s\n",upload_file,upload_name,buff);

    return 0;
}
#endif // FTPTOOL_H

首先包含上面的头文件 我这边叫做 ftptool.h
下面是使用的demo

const char * folder_name = resultForder.c_str(); //需要遍历的本地文件夹的名字
        DIR * dir = opendir(folder_name);
        int total=0;
        if(!dir) {
            perror("opendir");
            return;
        }
        else {
            struct dirent * ptr = NULL;
            char npath[1024];
            while ((ptr = readdir(dir))!=NULL) {

                if (!strcmp(ptr->d_name,".")||!strcmp(ptr->d_name, "..")) {
                    continue;
                }
                //如果文件类型是普通文件,进行上传
                if (ptr->d_type != DT_DIR) { 
                    std::string file_name(folder_name);
                    file_name.append("/");
                    file_name.append(ptr->d_name);
                    //ftp(ftpIp, ftpPort, username, password, local_filename, ftpFilename, ftpFolder)
                    //前四个参数是ftp服务器的ip,端口,用户名和密码,第五个参数是上传的本地文件的绝对路径,第五个参数是把此文件上传至ftp服务器之后的名称,第六个是上传至ftp的哪个文件夹下
                    ftp_upload_file("127.0.0.1",21,"xgd","123456",file_name.c_str(), ptr->d_name,folder_name);
                }
            }
        }

参考资料:
http://blog.sina.com.cn/s/blog_86d691b80100xu2s.html

结语:我这里相当于是进行反复的操作去建立连接,虽然每次都有释放,但是还是比较蠢的方式,之前试过建立一次连接,然后把文件都传上去,但是发现那种写法把所有文件的内容都写到了一个文件里,我猜测应该是需要多次通过PASV方式去请求到进行数据传输的端口

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巴塞罗那的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值