背景:Amazon S3提供了方便易用的对象存储接口,在对象存储中数据是以key-value的形式存放,对象之间默认没什么关联。在实际应用场景中,我们可能需要将一个文件夹上传到S3中,而不是一个一个文件地上传,并且期望保留文件夹里文件的层级关系。本文介绍三种方法,第三种用代码是重点。


1.使用S3Browser

在S3的客户端S3Browser中,我们发现可以upload floder,我将本地 H:\video\test 文件夹上传,test文件夹里有1.txt,2.txt两个文件和一个名为test2的文件夹,test2里有3.txt一个文件。使用S3Browser的upload floder将此文件夹上传后,可以看到图中效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结果是符合我们预期的。


2.使用s3cmd

用命令行查看上面操作的效果,发现Data这个bucket里多了一个/test/
在这里插入图片描述

我们再查看Data/test,可以看到test里有test2和两个对象1.txt,2.txt,也达到了预期。
在这里插入图片描述
实际上S3有上传文件夹命令:
s3cmd put -r yourfloder s3://yourbucket

比如 s3cmd put -r ceph-cluster s3://Data,把ceph-cluster上传:
在这里插入图片描述

在这里插入图片描述


3.使用API

尽管我们看到的上传文件夹后,在S3 bucket里有类似目录的东西,但是这并不是文件系统里那样的目录,只是S3用来表示层级关系的一种方式,在S3Browser中以图形界面的方式表现出来,并不是真正的我们常说的文件夹。

它只是通过 / 这个符号来表示层级关系
在S3提供的API中,并没有上传文件夹相关的方法,只有上传单个文件作为对象,源码接口如下:

PutObjectResult putObject(PutObjectRequest var1) throws SdkClientException, AmazonServiceException;

PutObjectResult putObject(String var1, String var2, File var3) throws SdkClientException, AmazonServiceException;

PutObjectResult putObject(String var1, String var2, InputStream var3, ObjectMetadata var4) throws SdkClientException, AmazonServiceException;

PutObjectResult putObject(String var1, String var2, String var3) throws AmazonServiceException, SdkClientException;

所以如果我们想通过代码上传文件夹,必须基于putObject()这个方法来写。


综上所述,要自己写代码上传文件夹,就要用 / 体现层次关系。所以这个问题转化成了——我们要获取子文件夹里的文件相对于父亲文件夹的路径,举个例子:我们要上传 *H:\video\test* 文件夹,其中的H:\video\test\test2\3.txt这个文件相对于H:\video\test\的路径表示就是\test\test2\3.txt。
代码的思路是:

  1. 输入文件夹路径,读取文件夹里文件,判断是文件还是子文件夹,如果是文件则记录其路径
  2. 如果是子文件夹,递归调用上面方法
  3. 对路径格式进行处理,转换成我们想要的格式,放在一个map里;注意开头不能带 /,否则S3里会出现名为 / 的文件夹
import s3service.S3ServiceImpl;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName: FloderUpload
 * @Description: TODO
 * @Author: PallasCat
 * @Version: V1.0
 * @Data: 2020/12/9 10:59 
 **/
public class FloderUpload {
    static Map<String,String>  flieMap = new HashMap<>();
    public static void main(String[] args) throws IOException {
        String path = "H:\\video\\test";
        String bucketName = "Data";
        uploadFloder(path,bucketName);
    }

    public static void uploadFloder(String floderPath, String bucketName) throws IOException {
        S3ServiceImpl s3Service=new S3ServiceImpl();
        s3Service.createConnect();
        String path = floderPath;
        String iniPath = new File(path).getParent()+File.separator;
        listAllFile(iniPath, path, flieMap);
        for(String absolutePath : flieMap.keySet()){
            System.out.println(absolutePath);
            System.out.println(flieMap.get(absolutePath));
            //s3Service是我自己根据官方接口putObject写的高层API
            s3Service.createObject(bucketName, flieMap.get(absolutePath), new File(absolutePath) );
        }
    }


    public static Map<String,String> listAllFile(String iniPath , String filePath, Map<String,String>  flieMap) throws IOException {
        if(!filePath.endsWith(File.separator)){
            filePath = filePath + File.separator;
        }
        File file = new File(filePath);
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File fileIndex : files){
                if(fileIndex.isDirectory()){
                    listAllFile(iniPath,fileIndex.getPath()+File.separator, flieMap);
                }
                else{
                    String aa = fileIndex.getPath();
                    String bb = file.getParent()+File.separator;
                    String cc = aa.replace(iniPath,"");
                    //Windows路径用\分割,Linux用/分割,所以需要转换
                    String dd = cc.replace("\\", "/");
                    //map的key是文件绝对路径,value是转换后的格式,用于S3对象名,可创造目录结构
                    flieMap.put(aa,dd);
                }
            }
        }
        return flieMap;
    }
}


输出如下:
在这里插入图片描述
上传结果同第一节用客户端上传的一样。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐