步骤一:创建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";
}
}