s3 ftp实现原理

S3 FTP 实现原理和使用方式

特性描述

s3 ftp 主要是实现对象存储协议转换成ftp协议

目的

为了提供ftp方式访问MOS对象存储,兼容客户使用场景。为客户更好的解决应用问题。

使用方式

Linux 客户端连接方式

 
连接ftp服务端
ftp 10.10.1.39

put 本地文件名 ftp服务端文件名  #上传文件到服务端

get 本地文件名 ftp服务端文件名 #从服务端下载文件

ls 参数  #遍历目录,参数为文件夹名字(可选参数)

cd 参数  #目录切换 参数为需切换的文件夹

pwd  #获取当前路径

rm 文件夹名字 #这个命令只能用于删除文件夹,删除文件无效,不能强制删除有文件的文件夹

delete 文件名 #用户删除文件名字

mkd 文件夹名字  #创建一个文件夹

具体的ftp指令集:http://blog.csdn.net/weiyuefei/article/details/51758288

windows 客户端连接方式

 输入账号密码就可以登录ftp服务端。

功能

主要功能列表如下:

1. 数据上传
2. 数据下载
3. 文件夹创建
4. 文件夹删除
5. 文件删除
6. 遍历文件夹
7. 获取文件大小
8. 二进制传输和ASCII传输
9. 目录切换
还不支持的功能如下:
1. 文件重命名
2. 文件夹重命名
3. 文件追加写
4. 文件所属用户权限
5. SSL加密传输

数据上传

客户端发送put请求,vsftp会得到put请求指令,然后调用具体put请求函数开始执行数据上传操作。
数据上传现在分为两种方式,如果文件大于16M就会自动启动分片上传。
在数据put时是不知道客户端发送数据的大小的。所以必须把数据缓存起来,先缓存16M数据,如果读完还没有16M,那就就直接把文件直接put到s3存储。否则使用分片,每片数据大小固定为4M,这个值是一个可配置参数。
 
// handle put op
if
(tunable_write_enable && (tunable_anon_upload_enable || !p_sess->is_anonymous) && str_equal_text(&p_sess->ftp_cmd_str, "STOR")) { if (tunable_s3_enable) handle_stor_s3(p_sess); else handle_stor(p_sess); }
// put or get op entra
// if is_recv = 1 is put otherwise is get
struct vsf_transfer_ret
vsf_ftpdataio_transfer_s3_file(struct vsf_session* p_sess, int remote_fd,
                                  struct mystr* bucket, struct mystr* object,
                                  filesize_t curr_offset, filesize_t num_send,
                                  int is_recv, int is_ascii)
{
  if (!is_recv)
  {
    if (is_ascii || p_sess->data_use_ssl)
    {
      return do_s3_file_send_rwloop(
        p_sess, bucket, object, curr_offset, is_ascii);
    }
    else
    {
      return do_s3_file_send_sendfile(
        p_sess, remote_fd, bucket, object, curr_offset, num_send);
    }
  }
  else
  {
    return do_s3_file_recv(p_sess, bucket, object, is_ascii);
  }
}

数据下载

客户端发送get请求,vsftp会得到get请求指令,然后调用具体get请求函数开始执行数据上传操作。

s3接口支持分片下载,可以get任意一段数据。所以无需做任何多余的缓存操作,只是不支持s3直接对客户端进行数据拷贝。

首先在get文件时,先判断文件是否存在。

// handle get op
if (tunable_download_enable &&
     str_equal_text(&p_sess->ftp_cmd_str, "RETR"))
    {
      if (tunable_s3_enable)
       handle_retr_s3(p_sess, 0);
      else
       handle_retr(p_sess, 0);
    }

文件夹创建

 文件夹创建分为两种情况一种是create bucket 一种是create object。如果是一级目录那就是创建bucket,如果不是就创建带"/"结束的object,里面不写入任何数据。

//handle mkd op
if (tunable_write_enable &&
   (tunable_anon_mkdir_write_enable || !p_sess->is_anonymous) &&
             (str_equal_text(&p_sess->ftp_cmd_str, "MKD") ||
              str_equal_text(&p_sess->ftp_cmd_str, "XMKD")))
    {
      if (tunable_s3_enable)
        handle_mkd_s3(p_sess);
      else
        handle_mkd(p_sess);
    }
// create bucket or create object

  if (str_isempty(&object))
    retval = vsf_s3_create_bucket(p_sess->s3_client, &bucket);
  else
    retval = vsf_s3_create_object(p_sess->s3_client, &bucket, &object);

文件夹删除

 文件夹删除是rm操作,这个操作不能用于文件,rm操作没有-f的参数所以只有当文件夹没有文件的时候才能删除成功,这里分为三种情况

删除bucket:直接调用删除bucket操作,如果有对象就会返回失败。

删除文件夹对象:先判断是否还有以这个文件夹为前缀的对象,如果有就删除失败。没有就直接删除这个文件夹对象。

只用判断是不是有这个为前缀的对象,如果有就表示目录存在,并且无法删除。

//handle rmd op
if (tunable_write_enable &&
             (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
             (str_equal_text(&p_sess->ftp_cmd_str, "RMD") ||
              str_equal_text(&p_sess->ftp_cmd_str, "XRMD")))
    {
      if (tunable_s3_enable)
        handle_rmd_s3(p_sess);
      else
        handle_rmd(p_sess);
    }
//remove bucket or remove object

  if (!str_isempty(&object))
  {
    retval = vsf_s3_dir_file(p_sess->s3_client, &bucket, &object);
    if (!retval)
    {
      vsf_cmdio_write(p_sess, FTP_FILEFAIL, 
                      "Remove directory operation failed.");
      goto out;
    }
  }
  
  if (str_isempty(&object))
    retval = vsf_s3_delete_bucket(p_sess->s3_client, &bucket);
  else
  {
    retval = vsf_s3_delete_object(p_sess->s3_client, &bucket, &object);
  }

文件删除

文件删除操作是属于delete操作,所带参数是文件的名字

//handle delete file op
if (tunable_write_enable &&
             (tunable_anon_other_write_enable || !p_sess->is_anonymous) &&
             str_equal_text(&p_sess->ftp_cmd_str, "DELE"))
    {
      if (tunable_s3_enable)
        handle_dele_s3(p_sess);
      else
        handle_dele(p_sess);
    }

遍历文件夹

 获取某个目录的所有文件和子文件夹,分两种情况,一种是list bucket,一种是list object。

list 也可能会带参数,如果不带参数就默认为list当前目录。

//handle list op
if (tunable_dirlist_enable &&
             str_equal_text(&p_sess->ftp_cmd_str, "LIST"))
    {
      if (tunable_s3_enable)
        handle_list_s3(p_sess);
      else
        handle_list(p_sess);
    }
//list bucket or list object
  while(is_truncate)
  {
    if (str_isempty(bucket))
    {
      failed = vsf_s3_list_bucket(p_sess->s3_client, &dir_list);
      is_truncate = 0;
    }
    else
    {
      failed = vsf_s3_list_object(p_sess->s3_client, bucket, object,
                                   1, &dir_list, &marker, &is_truncate);
    }
    if (!failed)
    {
      failed = write_dir_list(p_sess, &dir_list, target);
      str_list_free(&dir_list);
    }
    else
    {
      break;
    }
  }

获取文件大小

 size file 指令

//handle size file op
if (str_equal_text(&p_sess->ftp_cmd_str, "SIZE"))
    {
      if (tunable_s3_enable)
        handle_size_s3(p_sess);
      else
        handle_size(p_sess);
    }

 

目录切换

 cwd 指令,把当前目录切换到指定目录

// cdup or cwd
if (str_equal_text(&p_sess->ftp_cmd_str, "CWD") ||
             str_equal_text(&p_sess->ftp_cmd_str, "XCWD"))
    {
      if (tunable_s3_enable) 
        handle_cwd_s3(p_sess);
      else
        handle_cwd(p_sess);
    }
    else if (str_equal_text(&p_sess->ftp_cmd_str, "CDUP") ||
             str_equal_text(&p_sess->ftp_cmd_str, "XCUP"))
    {
      if (tunable_s3_enable)
        handle_cdup_s3(p_sess);
      else
        handle_cdup(p_sess);
    }
// find dir or file
static int
get_s3_object_path(struct mystr* p_str, struct mystr* curr_dir, int is_file)
{
  struct mystr curr_file = INIT_MYSTR;
  unsigned int i =0;
  unsigned int len = 0;
  int ret = 0;
  struct str_locate_result  res;
  
  if (!str_isempty(curr_dir) && str_get_char_at(p_str, 0) == '/')
    str_alloc_text(curr_dir, "/");

  len = str_getlen(p_str);
  if(len == 2 && str_equal_text(p_str, ".."))
  {
    if (is_file) 
    {
      ret = 1;
      goto out;
    }
    if (str_equal_text(curr_dir, "/"))
    {
      ret = 0;
      goto out;
    }
    if (!str_isempty(curr_dir) && 
        str_get_char_at(curr_dir, str_getlen(curr_dir) - 1) == '/')
      str_split_char_reverse(curr_dir, &curr_file, '/');
      
    res = str_locate_text_reverse(curr_dir, "/");
    if (!res.found)
    {
      ret = 1;
      goto out;
    }
    str_split_char_reverse(curr_dir, &curr_file, '/');
    if (str_isempty(curr_dir))
      str_append_char(curr_dir, '/');
    if (!str_isempty(curr_dir) && 
        str_get_char_at(curr_dir, str_getlen(curr_dir) - 1) != '/')
      str_append_char(curr_dir, '/');
    ret = 0;
    goto out;
  }
  
  if (!str_isempty(curr_dir) && 
       str_get_char_at(curr_dir, str_getlen(curr_dir) - 1) != '/')
    str_append_char(curr_dir, '/');
  
  for (i = 0; i < len; i++)
  {
    if (str_get_char_at(p_str, i) == '.')
    {
      if (str_get_char_at(p_str, i + 1) == '.' &&
          str_get_char_at(p_str, i + 2) == '/' && 
          (i + 2) < len)
      {
        if (str_equal_text(curr_dir, "/"))
        {
          i += 2;
          continue;
        }
        if (!str_isempty(curr_dir) && 
            str_get_char_at(curr_dir, str_getlen(curr_dir) - 1) == '/')
          str_split_char_reverse(curr_dir, &curr_file, '/');
        
        str_split_char_reverse(curr_dir, &curr_file, '/');
        i += 2;
        continue;
      }
      else if (str_get_char_at(p_str, i + 1) == '/' && (i + 1) < len)
      {
        continue;
      }
    }
    
    if (!str_isempty(curr_dir) && 
        str_get_char_at(curr_dir, str_getlen(curr_dir) - 1) == '/' && 
        str_get_char_at(p_str, i) == '/')
        continue;
    
    str_append_char(curr_dir, str_get_char_at(p_str, i));
    continue;
  }
  if (!str_isempty(curr_dir) && 
       str_get_char_at(curr_dir, str_getlen(curr_dir) - 1) != '/' && !is_file)
    str_append_char(curr_dir, '/');

out:
  str_free(&curr_file);
  return ret;
}

注意事项

根目录下面只能创建文件夹(因为一级文件夹是bucket,文件只能放在bucket里面)

 

posted on 2018-01-30 15:34  黎昊明  阅读(588)  评论(0)    收藏  举报

导航