【JAVA使用笔记】——大文件分割、内容处理

简述

对于提高大文件读写效率的方法网上数不胜数,我这里选择了使用NIO结合多线程处理的方式。

通过 io 的 RandomAccessFile 实现对大文件按行分片。

通过 nio 的 MappedByteBuffer 和 ByteBuffer 对缓冲区内的数据进行处理

通过nio 的 FileChannel,实现大文件的读写。

分片

实现在限制的分片大小(partitionSize)范围内,找出数据完整的切片,即行完整的切片。

大体逻辑是:

  • 确认分片起始位置(start)
  • 分片起始位置(start)+分片最大限制(partitionSize)得到待修正结束位置(preEnd)
  • 定位修正结束位置字符,向前遍历直到等于\r或\n
  • 以此作为分片结束位置(end)
  • 传递分片结束位置+1为下一次迭代开始位置

代码:

/**
     * 递归解决分区
     * @param result 结果集
     * @param filePointer 文件指针
     * @param start 本次开始位置
     * @param fileSize 文件大小
     * @param partitionSize 分区最大大小
     * @throws IOException 异常
     */
    private static void partitionHandle(LinkedList<FilePartition> result,
                                        RandomAccessFile filePointer,
                                        long start,
                                        long fileSize,
                                        long partitionSize) throws IOException{
        //新建分区 设置分区开始
        FilePartition partition = new FilePartition();
        partition.setStart(start);
        //判断结束是否在文件大小范围内
        long preEnd = start + partitionSize;
        if(preEnd < fileSize){
            //不越界获取preEnd-1位置字符,不是\r或者\n就倒车
            filePointer.seek(preEnd-1);
            byte b = filePointer.readByte();
            while (b != 13 && b != 10){
                preEnd = preEnd -1;
                filePointer.seek(preEnd);
                b = filePointer.readByte();
            }
            partition.setEnd(preEnd);
            result.add(partition);
            partitionHandle(result,filePointer,preEnd+1,fileSize,partitionSize);
        } else {
            //越界则以fileSize-1作为最后结束
            partition.setEnd(fileSize-1);
            result.add(partition);
        }

读取

使用 RandomAccessFile 打开文件,获得其 MappedByteBuffer

RandomAccessFile source = new RandomAccessFile(sourcePath,"r");
// 打开读取内存映射
MappedByteBuffer input = source.getChannel().map(
                    FileChannel.MapMode.READ_ONLY,
                    partition.getStart(),
                    partition.getEnd()-partition.getStart());

RandomAccessFile 获得 FileChannel 在进而获得 MappedByteBuffer,使用只读模式,从分片开始,读取分片大小的数据进行映射。

使用 byte[] 建立缓冲区从映射中拿出需要的数据,变成可处理的字符串

byte[] block = new byte[buffer_size];
while (offset < input.capacity()) {
    int block_size = input.capacity() - offset >= buffer_size ? buffer_size : input.capacity() - offset;
    for(int idx=0; idx < block_size;idx ++){
        block[idx] = input.get(offset+idx);
    }
    String data = new String(block,0,block_size);
    String[] lines = data.split("\n");
    ... ...                
    offset = offset + buffer_size;
}

写入

使用追加写的 FileOutputStream 获得FileChannel,使用 ByteBuffer 写入

FileOutputStream passFOS = new FileOutputStream(goodPath,true);
FileChannel passFC = passFOS.getChannel();
ByteBuffer pass_buffer = ByteBuffer.wrap(block);
pass_buffer.put(block);
pass_buffer.flip();
passFC.write(pass_buffer);
passFOS.flush();

性能

文件大小1281651KB约1.22G

对文件内数据行包含‘男性’和包含‘女性’字样的数据进行分堆

分区大小 268435456 约256M

缓冲区大小 26843546 约25.6M

耗时 11773ms 速度约为106M/S

文件上传是Web开发中常见的功能之一,Java中也提供了多种方式来实现文件上传。其中,一种常用的方式是通过Apache的commons-fileupload组件来实现文件上传。 以下是实现文件上传的步骤: 1.在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> ``` 2.在前端页面中添加文件上传表单: ```html <form method="post" enctype="multipart/form-data" action="upload"> <input type="file" name="file"> <input type="submit" value="Upload"> </form> ``` 3.在后台Java代码中处理上传文件: ```java // 创建一个DiskFileItemFactory对象,用于解析上传的文件 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置缓冲区大小,如果上传的文件大于缓冲区大小,则先将文件保存到临时文件中,再进行处理 factory.setSizeThreshold(1024 * 1024); // 创建一个ServletFileUpload对象,用于解析上传的文件 ServletFileUpload upload = new ServletFileUpload(factory); // 设置上传文件的大小限制,这里设置为10MB upload.setFileSizeMax(10 * 1024 * 1024); // 解析上传的文件,得到一个FileItem的List集合 List<FileItem> items = upload.parseRequest(request); // 遍历FileItem的List集合,处理上传的文件 for (FileItem item : items) { // 判断当前FileItem是否为上传的文件 if (!item.isFormField()) { // 获取上传文件文件名 String fileName = item.getName(); // 创建一个File对象,用于保存上传的文件 File file = new File("D:/uploads/" + fileName); // 将上传的文件保存到指定的目录中 item.write(file); } } ``` 以上代码中,首先创建了一个DiskFileItemFactory对象,用于解析上传的文件。然后设置了缓冲区大小和上传文件的大小限制。接着创建一个ServletFileUpload对象,用于解析上传的文件。最后遍历FileItem的List集合,判断当前FileItem是否为上传的文件,如果是,则获取文件名,创建一个File对象,将上传的文件保存到指定的目录中。 4.文件上传完成后,可以给用户一个提示信息,例如: ```java response.getWriter().write("File uploaded successfully!"); ``` 以上就是使用Apache的commons-fileupload组件实现文件上传的步骤。需要注意的是,文件上传可能会带来安全隐患,因此在处理上传的文件时,需要进行严格的校验和过滤。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值