OSS存储工具设计思路

步骤一:创建OSSClient实例并调用ListObjectsV2(GetBucketV2)接口用于列举存储空间(Bucket)中所有文件(Object)的信息。

参数

描述

方法

objectSummaries

返回的文件元信息。

List<OSSObjectSummary> getObjectSummaries()

prefix

本次查询结果的前缀。

String getPrefix()

ListObjectsV2(GetBucketV2)接口参数

@Autowired
    EhubService ehubService;
    // Endpoint为华东1(杭州)
    public static String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
    public static String accessKeyId = "";
    public static String accessKeySecret = "";
    // Bucket名称
    public static String bucketName = "";
    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

    @RequestMapping
    public void print() throws IOException {
        // 设置最大个数。
        int maxKeys = 1000;
        try {
            String nextContinuationToken = null;
            ListObjectsV2Result result;

            // 分页列举,每次传入上次返回结果中的nextContinuationToken。
            do {
                ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(bucketName).withMaxKeys(maxKeys);
                listObjectsV2Request.setFetchOwner(true);
                listObjectsV2Request.setContinuationToken(nextContinuationToken);

                result = ossClient.listObjectsV2(listObjectsV2Request);

                List<OSSObjectSummary> sums = result.getObjectSummaries();
                for (OSSObjectSummary s : sums) {
                    //每100条数据插入开一个线程
                    List<List<OSSObjectSummary>> lists = new ArrayList<>();
                    List<OSSObjectSummary> oSSObjectSummaryList = null;
                    for(int i = 0; i<sums.size();i++){
                        if(i%100 == 0){
                            oSSObjectSummaryList  = new ArrayList<>();
                            lists.add(oSSObjectSummaryList);
                        }
                        oSSObjectSummaryList.add(sums.get(i));
                    }
                    CountDownLatch countDownLatch = new CountDownLatch(lists.size());
                    for (List<OSSObjectSummary> listSub:lists) {
                        ehubService.analyse(listSub,countDownLatch);
                    }
                    try {
                        countDownLatch.await(); //保证之前的所有的线程都执行完成,才会走下面的;
                        // 这样就可以在下面拿到所有线程执行完的集合结果
                    } catch (Exception e) {
                        log.error("阻塞异常:"+e.getMessage());
                    }
                }
                nextContinuationToken = result.getNextContinuationToken();
            } while (result.isTruncated());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        //打印前4级目录的最大10个文件夹信息
        ehubService.print();
    }

步骤二:使用List<OSSObjectSummary> ossObjectSummaries接收,遍历获取所有文件名与相应大小。

参数

描述

方法

key

获取文件名

String getKey()

size

获取文件大小

String getSize()

ossObjectSummaries对象方法

步骤三:编写文件实体类Document,用于存储所有文件信息。属性名有:文件名,文件大小,所属路径。设计数据库文件表document:表结构为:主键,文件名,文件大小,所属路径。

字段名

描述

类型

是否可为空

默认值

document_id

主键

int

document_name

文件名

varchar

document_size

文件大小

int

0

document_path

所属路径

varchar

一级文件

文件表document表结构

@Data
@TableName(value = "document")
public class Document {
    @TableId(type = IdType.AUTO)
    private Long documentId;
    private String documentName;
    private Long documentSize;
    private String documentPath;

    public Document(String documentName, Long documentSize, String documentPath) {
        this.documentName = documentName;
        this.documentSize = documentSize;
        this.documentPath = documentPath;
    }
}

步骤四:编写文件夹实体类Folder,用于存储所有路径信息。属性名有:文件夹名,文件夹大小,所含文件数,所含文件夹数。设计数据库文件表folder:表结构为:主键,文件夹名,文件夹大小,所含文件数,所含文件夹数,所含文件夹路径,所属路径。

字段名

描述

类型

是否可为空

默认值

folder_id

主键

int

folder_name

文件夹名

varchar

folder_size

文件夹大小

int

0

folder_number_of_files

所含文件数

int

0

folder_number_of_folders

所含文件夹数

int

0

folder_path_of_folders

所含文件夹路径

varchar

folder_path

所属路径

varchar

一级文件夹

文件夹表folder表结构

@Data
@TableName(value = "folder")
public class Folder {
    @TableId(type = IdType.AUTO)
    private Long folderId;
    private String folderName;//文件夹名
    private Long folderSize;//文件夹大小
    private Integer folderNumberOfFiles;//所含文件数
    private Integer folderNumberOfFolders;//所含文件夹数
    private String folderPathOfFolders;//所含文件夹路径
    private String folderPath;//所属路径

    public Folder(String folderName, Long folderSize, Integer folderNumberOfFiles, Integer folderNumberOfFolders, String folderPathOfFolders, String folderPath) {
        this.folderName = folderName;
        this.folderSize = folderSize;
        this.folderNumberOfFiles = folderNumberOfFiles;
        this.folderNumberOfFolders = folderNumberOfFolders;
        this.folderPathOfFolders = folderPathOfFolders;
        this.folderPath = folderPath;
    }

    public Folder() {
    }

步骤五:每一次从oss中获取一个新的文件之后使用String的split() 方法将每一级路径进行拆分为数组除去最后一个字符串为所属路径,最后一个字符串为文件名,根据步骤二获取文件大小并全部存于document表中。同理可得到该文件的每一级文件夹名与所属路径,同时在folder表中查询是否存在文件夹名与所属路径相同的数据,如果有:更新文件夹大小,所含文件数+1;【之后查询该文件夹所含文件夹路径中是否含有文件夹完整路径,如果没有:所含文件夹数+1】否则(添加新文件夹):更新文件夹大小,所含文件数+1,所含文件夹数+1,添加所属路径。最后存入folder表中。

    //对数据进行分析并赋予文件夹属性值
    @Async("ehub2")
    public void analyse(List<OSSObjectSummary> listSub, CountDownLatch countDownLatch) {
        try {
            for (OSSObjectSummary s : listSub) {
                //将路径进行拆分得到每一级文件夹
                String a[] = s.getKey().split("/");
                //所属路径
                String path = "";
                //文件夹下级目录
                String path1;
                if (a.length > 1) {
                    for (int i = 0; i < a.length - 1; i++) {
                        //文件夹路径
                        if (i != 0) {
                            path = path + a[i - 1] + "/";
                        } else {
                            path = "";
                        }
                        //文件夹名
                        String name = a[i];
                        //文件夹下级目录
                        if (i < a.length - 2) {
                            path1 = path + name + "/" + a[i + 1] + "/";
                        } else {
                            path1 = "最后一级文件夹";
                        }

                        //编程式事务
                        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
                        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
                        TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);
                        try {
                            //判断数据库中是否有该文件夹(文件夹名相同且路径相同)
                            Folder folder = folderMapper.findFolderByName(name, path);
                            if (folder != null) {//如果有:更新文件夹大小,所含文件数+1;
                                folder.setFolderSize(folder.getFolderSize() + s.getSize());
                                folder.setFolderNumberOfFiles(folder.getFolderNumberOfFiles() + 1);
                                //之后查询该文件夹所含文件夹路径中是否含有文件夹完整路径,如果没有:所含文件夹数+1
                                String b[] = folder.getFolderPathOfFolders().split(",");
                                if (!Arrays.asList(b).contains(path1) && path1 != "最后一级文件夹") {
                                    folder.setFolderNumberOfFolders(folder.getFolderNumberOfFolders() + 1);
                                    //添加该目录
                                    folder.setFolderPathOfFolders(folder.getFolderPathOfFolders() + "," + path1);
                                }
                                //更新至数据库
                                folderMapper.upDateFolder(folder);
                            } else { //如果没有(添加新文件夹):更新文件夹大小,所含文件数+1,添加所属路径。最后存入folder表中
                                //通过构造函数进行赋值
                                Folder folder1 = new Folder(name, s.getSize(), 1, 0, "", path);
                                //对path进行拆分,对每一级的文件文件夹所含文件夹数量加一
                                String b[] = path.split("/");
                                String path2 = "";
                                for (int j = 0; j < b.length; j++) {
                                    if (j > 0) {
                                        path2 = path2 + b[j - 1] + "/";
                                    }
                                    if (j < b.length - 1) {
                                        //更新数据
                                        Folder folder2 = folderMapper.findFolderByName(b[j], path2);
                                        folder2.setFolderNumberOfFolders(folder2.getFolderNumberOfFolders() + 1);
                                        //保存至数据库
                                        folderMapper.deleteById(Math.toIntExact(folder2.getFolderId()));
                                        folderMapper.insert(folder2);
                                    }
                                }
                                //保存至数据库
                                folderMapper.insert(folder1);
                            }
                            transactionManager.commit(transactionStatus);
                        } catch (Exception e) {
                            log.error("xxxx", e);
                            transactionManager.rollback(transactionStatus);
                        }
                    }
                }
            }
        } finally {
            countDownLatch.countDown();// 很关键, 无论上面程序是否异常必须执行countDown,否则await无法释放
        }
    }

步骤六:在实体类Folder中添加一个Arraylist list<Folder>集合用于存储当前文件夹中最大的10个文件夹,并创建一个方法打印相关信息

@Data
public class FolderVo {
        @TableId(type = IdType.AUTO)
        private Long folderId;
        private String folderName;//文件夹名
        private Long folderSize;//文件夹大小
        private Integer folderNumberOfFiles;//所含文件数
        private Integer folderNumberOfFolders;//所含文件夹数
        private String folderPathOfFolders;//所含文件夹路径
        private String folderPath;//所属路径
        private List<FolderVo> folderVos;//大小最大的10个文件夹
}

步骤七:在数据库中对一级文件夹大小进行排序,选取前十个文件夹存储与步骤六的类中。

    @Select("select * from folder  where folder_path = #{path} order by folder_size desc  limit 0,10 ")
    List<FolderVo> findFolderBySize(String path);

步骤八:对所含文件夹路径的文件夹大小进行排序,选取前十个文件夹存储与步骤六的类中。循环该操作至所含文件夹数为0。

步骤九:打印报告

    //打印
    public void write(List<FolderVo> folderVos, OutputStreamWriter dos) throws IOException {
        for (int i = 0; i < folderVos.size(); i++) {
            //打印空格
            for (int j = 0; j < line; j++) {
                dos.write("        ");
            }
            dos.write((i + 1) + "." + "第" + (line + 1) + "级文件夹:" + folderVos.get(i).getFolderName() + "      ");
            dos.write("文件夹大小:" + math(folderVos.get(i).getFolderSize()) + "      ");
            dos.write("含有文件个数:" + folderVos.get(i).getFolderNumberOfFiles() + "      ");
            dos.write("含有文件夹个数:" + folderVos.get(i).getFolderNumberOfFolders() + "      ");
            dos.write("所属路径为:" + folderVos.get(i).getFolderPath() + "      ");
            dos.write("\r\n");
            //下一层
            line++;
            //设置需要访问的层数
            if (line <= 4) {
                write(folderVos.get(i).getFolderVos(), dos);
            }
            //返回上一级
            line--;
        }
    }

    //将文件夹大小转换为合适位数
    public String math(Long size) {
        if (size / 1024 < 1) {
            return size + "B";
        } else if (size / 1024 / 1024 < 1) {
            return size / 1024 + "KB";
        } else if (size / 1024 / 1024 / 1024 < 1) {
            return size / 1024 / 1024 + "MB";
        } else {
            return size / 1024 / 1024 / 1024 + "G";
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kblzxj

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

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

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

打赏作者

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

抵扣说明:

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

余额充值