2021年3月5日星期五

怎样将大批量文件进行循环分组(reduce)?

背景

   当有时候一个文件夹下有几万个几十万个文件时,我们的桌面终端打开这个文件夹可能会卡。或者将文件进行批量上传时,如果是在文件夹下全选,那么基本上浏览器就卡死了,当然也不能这样子操作滴~

   题主最近就遇到这样一个问题,批量上传文件,有几万个,担心全选会搞崩浏览器或者cmd终端,于是打算将数据分组,分批次上传,减少风险压力。可能有的同学会说,那简单嘛,直接ctrl C+V完事儿,但是人这个眼睛呐,越集中注意力看一个字,就越不觉得它像个字,所以难免会出错的,而且拖动也会很卡。

作为一个搞电脑的工程师(程序猿),能用电脑解决的,怎么能浪费体力呢[滑稽]

参考步骤

   其实要实现这么一个东西,很简单的,二话不多说,直接一个for循环搞定 欧耶!但是呀,那个速度呀,难以忍受,如果分组这个文件还需要去做一些额外的操作,那岂不是更慢,说到这,想到以前读书学习大数据的时候,分词计算map-reduce那每次一跑就是一个小时过去了,所以,光写出来不得行,还得优化。而且分组的时候有一些注意点要注意。

   题主的大致步骤如下:

  1. 打开文件夹,遍历文件;
  2. 启用线程池,多线程跑批任务,加快速度;
  3. 计数文件夹中文件个数,达到指定数量,建立下一个文件夹;
  4. 使用新的文件夹继续移动或复制文件,操作过程中进行重命名;重复3,4步骤
  5. 操作完毕,等待线程池任务处理完毕,销毁。

代码实现

   说了那么多,还是直接上代码吧:)

import java.io.File;import java.util.Arrays;import java.util.Objects;import java.util.concurrent.*;public class TestReduceFile { /**  * 文件计数  */ private static volatile int currentFileNum = 0; private static final String initFilePathName = "/Users/anhaoo/test/reduceFile/reduce"; private static volatile String currentFilePathName; public static void main(String[] args) {  groupFile("/User/anhaoo/test/big_pdf"); } /**  * 将文件分组:分成一个文件夹多少个文件这种  * @param oldFilePath  */ private static void groupFile(String oldFilePath) {  File file = new File(oldFilePath);  File[] fileList = file.listFiles();  // 线程池批处理:链表阻塞队列  ExecutorService executor = Executors.newFixedThreadPool(80);  if (Objects.nonNull(fileList) && fileList.length > 0) {   // System.out.println(fileList.length);   Arrays.stream(fileList).forEach(item -> {    // 线程池提交任务处理    if (!item.isDirectory()) {     executor.execute(() -> groupFileSub(item));    }   });  }  // shutdown 等待任务全部执行完毕 销毁  executor.shutdown(); } /**  * 分组文件,每满n个文件生成下一个文件夹,然后将后续遍历的文件移动到下一个文件夹中  * @param oldFile  */ private static synchronized void groupFileSub(File oldFile) {  // 判断是否需要新生成文件夹,一个文件夹下放700个  if (currentFileNum % 700 == 0) {   // reduce0,reduce1,reduce2......   String newFileFolder = initFilePathName.concat(String.valueOf(currentFileNum/700));   // 将新的文件夹名赋值给共享变量   currentFilePathName = newFileFolder;  }  File aimFilePath = new File(currentFilePathName);  if (!aimFilePath.exists()) {   aimFilePath.mkdirs();  }  try {   // 移动文件时顺便重命名文件   String oldFileName = oldFile.getName();   String[] fileNameArr = oldFileName.split("_");   // 加下面这个if的原因 是因为mac电脑下有一个隐藏的.DS_Store文件,它按我的规则重命名时会影响我的操作   // 所以判断一下,不然我这里会抛越界异常:)   if (fileNameArr.length < 3) {    return;   }   String uuid = fileNameArr[2];   String newFileName = "/pdfFile_".concat(uuid);   File aimFile = new File(aimFilePath + File.separator + newFileName);   currentFileNum++;   oldFile.renameTo(aimFile);  } catch (Exception e) {   e.printStackTrace();  } }}

   代码就如上了,效果如图所示:

701 是因为有一个隐藏文件了哈!

注意事项

   有几个点需要注意一下:上面程序中使用了volatile和synchronized,以及线程池等工具,

  • 使用线程池,是为了多个线程一起处理,加快效率;
  • 使用volatile修饰了两个变量,因为currentFileNum会实时变化,currentFilePathName文件夹也会在执行过程中发生变化

   可能有的同学会有疑惑,既然使用了volatile关键保证了多线程变量的可见性,那为什么还要使用synchronize开持锁同步呢?哈哈哈,刚开始我也是这么认为的,没有使用锁,直接跑,但是每次跑完后每个文件夹的数量不仅不相等而且数量还不一致,不止700个多,后来仔细一想,volatile虽然保持了变量的可见性,但是当多个线程拿到这个变量是将变量副本拷贝到自己的栈内存中,只能保证在获取变量的时候是最新的,但是CPU指令的执行是哪个线程抢到了就去执行,所以可能刚好那个时候其他线程又将变量修改了,因为计数变量currentFileNum在不停地自增,导致线程不安全,不符合我们的预期效果,这个也再一次证明了一个结论:

volatile只是保证了可见性,但是线程变量的安全它无法保证;

所以在这个方法上加了一个锁,保证线程的安全性;

   上述就是本文想分享的东西了,如果有更好的方法或者文章中有不足之处欢迎大家指出,共同进步才是我们的宗旨!









原文转载:http://www.shaoqun.com/a/606596.html

跨境电商:https://www.ikjzd.com/

识货:https://www.ikjzd.com/w/1745

旺店通:https://www.ikjzd.com/w/2390


背景   当有时候一个文件夹下有几万个几十万个文件时,我们的桌面终端打开这个文件夹可能会卡。或者将文件进行批量上传时,如果是在文件夹下全选,那么基本上浏览器就卡死了,当然也不能这样子操作滴~   题主最近就遇到这样一个问题,批量上传文件,有几万个,担心全选会搞崩浏览器或者cmd终端,于是打算将数据分组,分批次上传,减少风险压力。可能有的同学会说,那简单嘛,直接ctrlC+V完事儿,但是人这个眼睛呐
败欧洲运费:https://www.ikjzd.com/w/1555
wangwei:https://www.ikjzd.com/w/1744
outfit:https://www.ikjzd.com/w/938
如何获取站内精准客户信息?如何利用这些信息进行站外推广?:https://www.ikjzd.com/home/17294
口述:出轨后想离婚 我雇小姐色诱老公:http://lady.shaoqun.com/m/a/7604.html
OnBuy要崛起?英国新电商平台OnBuy率先推出PayPal for Marketplaces服务!:https://www.ikjzd.com/home/9471

没有评论:

发表评论