Java多线程实现快速切分文件的程序

 更新时间:2016年06月08日 15:18:48   投稿:lijiao  
这篇文章主要为大家详细介绍了Java多线程实现快速切分文件的相关资料,感兴趣的小伙伴们可以参考一下

前段时间需要进行大批量数据导入,DBA给提供的是CVS文件,但是每个CVS文件都好几个GB大小,直接进行load,数据库很慢还会产生内存不足的问题,为了实现这个功能,写了个快速切分文件的程序。

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
 
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
 
public class FileSplitUtil {
 
  private final static Logger log = LogManager.getLogger(FileSplitUtil.class);
  private static final long originFileSize = 1024 * 1024 * 100;// 100M
  private static final int blockFileSize = 1024 * 1024 * 64;// 防止中文乱码,必须取2的N次方
  /**
   * CVS文件分隔符
   */
  private static final char cvsSeparator = '^';
  public static void main(String args[]){
    long start = System.currentTimeMillis();
    try {
      String fileName = "D:\\csvtest\\aa.csv";
      File sourceFile = new File(fileName);
      if (sourceFile.length() >= originFileSize) {
        String cvsFileName = fileName.replaceAll("\\\\", "/");
        FileSplitUtil fileSplitUtil = new FileSplitUtil();
        List<String> parts=fileSplitUtil.splitBySize(cvsFileName, blockFileSize);
        for(String part:parts){
          System.out.println("partName is:"+part);
        }
      }
      System.out.println("总文件长度"+sourceFile.length()+",拆分文件耗时:" + (System.currentTimeMillis() - start) + "ms.");
    }catch (Exception e){
      log.info(e.getStackTrace());
    }
 
  }
 
 
 
  /**
   * 拆分文件
   *
   * @param fileName 待拆分的完整文件名
   * @param byteSize 按多少字节大小拆分
   * @return 拆分后的文件名列表
   */
  public List<String> splitBySize(String fileName, int byteSize)
      throws IOException, InterruptedException {
    List<String> parts = new ArrayList<String>();
    File file = new File(fileName);
    int count = (int) Math.ceil(file.length() / (double) byteSize);
    int countLen = (count + "").length();
    RandomAccessFile raf = new RandomAccessFile(fileName, "r");
    long totalLen = raf.length();
    CountDownLatch latch = new CountDownLatch(count);
 
    for (int i = 0; i < count; i++) {
      String partFileName = file.getPath() + "."
          + leftPad((i + 1) + "", countLen, '0') + ".cvs";
      int readSize=byteSize;
      long startPos=(long)i * byteSize;
      long nextPos=(long)(i+1) * byteSize;
      if(nextPos>totalLen){
        readSize= (int) (totalLen-startPos);
      }
      new SplitRunnable(readSize, startPos, partFileName, file, latch).run();
      parts.add(partFileName);
    }
    latch.await();//等待所有文件写完
    //由于切割时可能会导致行被切断,加工所有的的分割文件,合并行
    mergeRow(parts);
    return parts;
  }
 
  /**
   * 分割处理Runnable
   *
   * @author supeidong
   */
  private class SplitRunnable implements Runnable {
    int byteSize;
    String partFileName;
    File originFile;
    long startPos;
    CountDownLatch latch;
    public SplitRunnable(int byteSize, long startPos, String partFileName,
               File originFile, CountDownLatch latch) {
      this.startPos = startPos;
      this.byteSize = byteSize;
      this.partFileName = partFileName;
      this.originFile = originFile;
      this.latch = latch;
    }
 
    public void run() {
      RandomAccessFile rFile;
      OutputStream os;
      try {
        rFile = new RandomAccessFile(originFile, "r");
        byte[] b = new byte[byteSize];
        rFile.seek(startPos);// 移动指针到每“段”开头
        int s = rFile.read(b);
        os = new FileOutputStream(partFileName);
        os.write(b, 0, s);
        os.flush();
        os.close();
        latch.countDown();
      } catch (IOException e) {
        log.error(e.getMessage());
        latch.countDown();
      }
    }
  }
 
  /**
   * 合并被切断的行
   *
   * @param parts
   */
  private void mergeRow(List<String> parts) {
    List<PartFile> partFiles = new ArrayList<PartFile>();
    try {
      //组装被切分表对象
      for (int i=0;i<parts.size();i++) {
        String partFileName=parts.get(i);
        File splitFileTemp = new File(partFileName);
        if (splitFileTemp.exists()) {
          PartFile partFile = new PartFile();
          BufferedReader reader=new BufferedReader(new InputStreamReader(new FileInputStream(splitFileTemp),"gbk"));
          String firstRow = reader.readLine();
          String secondRow = reader.readLine();
          String endRow = readLastLine(partFileName);
          partFile.setPartFileName(partFileName);
          partFile.setFirstRow(firstRow);
          partFile.setEndRow(endRow);
          if(i>=1){
            String prePartFile=parts.get(i - 1);
            String preEndRow = readLastLine(prePartFile);
            partFile.setFirstIsFull(getCharCount(firstRow+preEndRow)>getCharCount(secondRow));
          }
 
          partFiles.add(partFile);
          reader.close();
        }
      }
      //进行需要合并的行的写入
      for (int i = 0; i < partFiles.size() - 1; i++) {
        PartFile partFile = partFiles.get(i);
        PartFile partFileNext = partFiles.get(i + 1);
        StringBuilder sb = new StringBuilder();
        if (partFileNext.getFirstIsFull()) {
          sb.append("\r\n");
          sb.append(partFileNext.getFirstRow());
        } else {
          sb.append(partFileNext.getFirstRow());
        }
        writeLastLine(partFile.getPartFileName(),sb.toString());
      }
    } catch (Exception e) {
      log.error(e.getMessage());
    }
  }
 
  /**
   * 得到某个字符出现的次数
   * @param s
   * @return
   */
  private int getCharCount(String s) {
    int count = 0;
    for (int i = 0; i < s.length(); i++) {
      if (s.charAt(i) == cvsSeparator) {
        count++;
      }
    }
    return count;
  }
 
  /**
   * 采用BufferedInputStream方式读取文件行数
   *
   * @param filename
   * @return
   */
  public int getFileRow(String filename) throws IOException {
    InputStream is = new BufferedInputStream(new FileInputStream(filename));
    byte[] c = new byte[1024];
    int count = 0;
    int readChars = 0;
    while ((readChars = is.read(c)) != -1) {
      for (int i = 0; i < readChars; ++i) {
        if (c[i] == '\n')
          ++count;
      }
    }
    is.close();
    return count;
  }
 
  /**
   * 读取最后一行数据
   * @param filename
   * @return
   * @throws IOException
   */
  private String readLastLine(String filename) throws IOException {
    // 使用RandomAccessFile , 从后找最后一行数据
    RandomAccessFile raf = new RandomAccessFile(filename, "r");
    long len = raf.length();
    String lastLine = "";
    if(len!=0L) {
      long pos = len - 1;
      while (pos > 0) {
        pos--;
        raf.seek(pos);
        if (raf.readByte() == '\n') {
          lastLine = raf.readLine();
          lastLine=new String(lastLine.getBytes("8859_1"), "gbk");
          break;
        }
      }
    }
    raf.close();
    return lastLine;
  }
  /**
   * 修改最后一行数据
   * @param fileName
   * @param lastString
   * @return
   * @throws IOException
   */
  private void writeLastLine(String fileName,String lastString){
    try {
      // 打开一个随机访问文件流,按读写方式
      RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw");
      // 文件长度,字节数
      long fileLength = randomFile.length();
      //将写文件指针移到文件尾。
      randomFile.seek(fileLength);
      //此处必须加gbk,否则会出现写入乱码
      randomFile.write(lastString.getBytes("gbk"));
      randomFile.close();
    } catch (IOException e) {
      log.error(e.getMessage());
    }
  }
  /**
   * 左填充
   *
   * @param str
   * @param length
   * @param ch
   * @return
   */
  public static String leftPad(String str, int length, char ch) {
    if (str.length() >= length) {
      return str;
    }
    char[] chs = new char[length];
    Arrays.fill(chs, ch);
    char[] src = str.toCharArray();
    System.arraycopy(src, 0, chs, length - src.length, src.length);
    return new String(chs);
  }
 
  /**
   * 合并文件行内部类
   */
  class PartFile {
    private String partFileName;
    private String firstRow;
    private String endRow;
    private boolean firstIsFull;
 
    public String getPartFileName() {
      return partFileName;
    }
 
    public void setPartFileName(String partFileName) {
      this.partFileName = partFileName;
    }
 
    public String getFirstRow() {
      return firstRow;
    }
 
    public void setFirstRow(String firstRow) {
      this.firstRow = firstRow;
    }
 
    public String getEndRow() {
      return endRow;
    }
 
    public void setEndRow(String endRow) {
      this.endRow = endRow;
    }
 
    public boolean getFirstIsFull() {
      return firstIsFull;
    }
 
    public void setFirstIsFull(boolean firstIsFull) {
      this.firstIsFull = firstIsFull;
    }
  }
 
}

以上就是本文的全部内容,希望对大家学习java程序设计有所帮助。

相关文章

  • springAOP完整实现过程

    springAOP完整实现过程

    当你调用SimpleService类的doSomething方法时,上述的PerformanceAspect会自动拦截此调用,并且记录该方法的执行时间,这样你就完成了一个针对Spring的AOP入门级案例,感兴趣的朋友一起看看吧
    2024-02-02
  • JavaIO BufferedReader和BufferedWriter使用及说明

    JavaIO BufferedReader和BufferedWriter使用及说明

    这篇文章主要介绍了JavaIO BufferedReader和BufferedWriter使用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • Java动态规划之硬币找零问题实现代码

    Java动态规划之硬币找零问题实现代码

    这篇文章主要介绍了Java动态规划之硬币找零问题实现代码,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • java web返回中文乱码问题及解决

    java web返回中文乱码问题及解决

    这篇文章主要介绍了java web返回中文乱码问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • springboot通过spel结合aop实现动态传参的案例

    springboot通过spel结合aop实现动态传参的案例

    SpEl 是Spring框架中的一个利器,Spring通过SpEl能在运行时构建复杂表达式、存取对象属性、对象方法调用等,今天通过本文给大家介绍springboot spel结合aop实现动态传参,需要的朋友可以参考下
    2022-07-07
  • 详解Nacos中注册中心和配置中心的实现

    详解Nacos中注册中心和配置中心的实现

    Spring Cloud Alibaba 是阿里巴巴提供的一站式微服务开发解决方案。而 Nacos 作为 Spring Cloud Alibaba 的核心组件之一,提供了两个非常重要的功能:注册中心和配置中心,我们今天来了解和实现一下二者
    2022-08-08
  • 关于工厂方法模式的Java实现

    关于工厂方法模式的Java实现

    这篇文章主要介绍了关于工厂方法模式的Java实现讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • SpringBoot中的@CrossOrigin注解详解

    SpringBoot中的@CrossOrigin注解详解

    这篇文章主要介绍了SpringBoot中的@CrossOrigin注解详解,跨源资源共享(CORS)是由大多数浏览器实现的W3C规范,允许您灵活地指定什么样的跨域请求被授权,而不是使用一些不太安全和不太强大的策略,需要的朋友可以参考下
    2023-11-11
  • springboot项目中idea的pom.xml文件的引用标签全部爆红问题解决

    springboot项目中idea的pom.xml文件的引用标签全部爆红问题解决

    这篇文章主要介绍了springboot项目中idea的pom.xml文件的引用标签全部爆红问题解决,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧
    2023-12-12
  • IDEA 2023创建JSP项目的完整步骤教程

    IDEA 2023创建JSP项目的完整步骤教程

    这篇文章主要介绍了IDEA 2023创建JSP项目的完整步骤教程,创建项目需要经过新建项目、设置项目名称和路径、选择JDK版本、添加模块和工件、配置Tomcat服务器等步骤,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2024-10-10

最新评论