测试利用多线程进行文件的写操作

最近学习NIO技术,了解了有关channel通道、buffer缓存以及selector选择器等技术,萌发了想写一个多点下载的一个简单测试demo。我将这个demo分成两步,第一步先实现将一个文件分段复制到一个文件中(通常我们是将文件以流的形式一个字节一个字节的复制到目标文件中,现在我们是将文件分段,启用多个线程,每个线程复制一部分,然后再根据原文件分段的位置组装成一个文件,实现高效的目的)。下面帖源码

/**

* 测试利用多线程进行文件的写操作

* @author gcl

* @version 2015年7月6日

* @see Test

* @param

* @since

*/

package com.file.nio.test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;

public class TransferTest {

static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) throws Exception {
    TransferTest.avgFile("D://demo.log");

}
private static void avgFile(String filePath) throws IOException{
     File file = new File(filePath);
     String fileName = file.getName();
     /**
      * RandomAccessFile支持任意访问,可以指定到文件的任意位置进行读写操作,该类只支持对文件
      * 的操作,不支持其他流
      * 初始化该类的第二个参数r标示只读,rw标示支持读写
      */
     RandomAccessFile raf = new RandomAccessFile(file, "r");
     /**
      * 可以看见该文件有多少个字节(byte)
      */
     long length = raf.length();
     System.out.println("文件长度"+length/1024);
     /**
      * 通过RandomAccessFile对象获取一个通道,获取这个通道主要是想用它的映射功能
      * FileChannel是个抽象类,只能通过InputStream、OutputStream的实现类来getChannel()来
      * 获取,
      * 比如FileInputStream等等
      * 当然还包括RandomAccessFile的getChannel()
      */
     FileChannel channel=raf.getChannel();
     /**
      * 将文件分成等比例的5分,因为我们打算启动5个线程分别复制,因此这里除以5
      */
     long avg = length/5;
     /**
      * 通过进一法取整,要不然除下来,每个线程传13.5byte也不合适,必须是整数字节吧;
      * 但是通过进一法取整的话,最后5个想加,很可能大于原文件大小,我们后面会处理,
      * 无非就是最后一个线程只传前四个线程传输剩下的部分么,就不用这个进一法取整的值了
      */
     int c = (int)Math.ceil(avg);
     int i = 0;
     /**
      * 循环启动5个线程
      */
     while(i < 5){
         /**
          * 这是计算每个线程从文件的那个字节开始复制,我们说了RandomAccessFile
          * 支持从指定位置读写操作嘛
          */
         int skip = c*i;
         /**
          * 这里就是我们判断如果最后一个线程读的字节长度大于文件长度时,直接取文件剩下部分
          */
         if(skip > length)
             skip = (int)length;
         /**
          * 这就是通过通道channel的map来复制文件的一部分到内存中,得到的是一个
          * MappedByteBuffer缓存
          * 主意下面几个参数的含义,
          * 1:第一个参数标示只读,当然还有MapMode.READ_WRITE标示可读可写,
          *    什么数据:。
          * 2:第二个参数标示将指针指到文件的那个字节。
          * 3:第三个参数标示从skip个位置开始,映射c长度的一段数据到内存,供程序操作
          */
         MappedByteBuffer  buffer = channel.map(MapMode.READ_ONLY, skip, c);
         new FileWriteThread(skip,buffer,fileName).start();
         i++;
         //清空缓存,因为我们循环要用
         buffer.clear();
     }
     //关闭RandomAccessFile
     raf.close();
     //关闭FileChannel
     channel.close();
}
// 利用线程在文件的指定位置写入指定数据
static class FileWriteThread extends Thread{
    //复制文件的开始位置
    private int skip;
    //字节缓存
    private MappedByteBuffer buffer;
    //文件名称
    private String fileName;

    public FileWriteThread(int skip,MappedByteBuffer buffer,String fileName){
        this.skip = skip;
        this.buffer = buffer;
        this.fileName = fileName;
    }

    public void run(){
        RandomAccessFile raf = null;
        try {
            /**
             * RandomAccessFile初始化时,指定的文件路径,如果不存在,则会创建一个,第二个参数
             * rw标示支持读写操作
             */
            raf = new RandomAccessFile("E://"+fileName, "rw");
            /**
             * 将指针定位到skip的位置,开始写入数据
             */
            raf.seek(skip);
            /**
             * buffer.remaining()简单的理解就是根据buffer缓存区数据大小创建
             * 相应大小的byet数组
             */
            byte[] b = new byte[buffer.remaining()];
            /**
             * 下面这个方法还是贴官网解释吧
             * public ByteBuffer get(byte[] dst,int offset,int length)
             *  所需的字节(即如果 length > remaining()),则不传输字节且抛出
             *  BufferUnderflowException。否则,此方法将此缓冲区中的 length 个字节复制到给定
             *    将增加 length。
             * 换句话说,调用此方法的形式为 src.get(dst, off, len),效果与以下循环语句完全相同:
             *   for (int i = off; i < off + len; i++)
             *         dst[i] = src.get();
             * 区别在于它首先检查此缓冲区中是否具有足够的字节,这样可能效率更高。
             * 参数:
             *  dst - 向其中写入字节的数组
             *  offset - 要写入的第一个字节在数组中的偏移量;必须为非负且不大于 dst.length
             *  length - 要写入到给定数组中的字节的最大数量;必须为非负且不大于 dst.length - offset
             */
            buffer.get(b, 0, b.length);
            /**
             * 开始通过RandomAccessFile写入文件
             */
            raf.write(b);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭RandomAccessFile
                raf.close();
            } catch (Exception e) {
            }
        }
    }
}

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-27 10:53:15

测试利用多线程进行文件的写操作的相关文章

iOS实现文件的写操作

// //  main.m //  NSFileHandleDemo2-写 // //  Created by GuoYule on 15/2/19. //  Copyright (c) 2015年 GuoYule. All rights reserved. // //这是一个写文件 #import <Foundation/Foundation.h> #define PATH @"/Users/guoyule/Desktop/guoyule.rtf" int main(in

第24讲 python文件的写操作基础

write---> 写字符串 writelines--->写字符串序列(序列:一堆字符串,逗号隔开.例如:字典.列表.元组) file_obj.write(content_obj) content_obj + '\n'  #me:如果不加 \n 则写入的内容不换行 eg1: file_obj=open('test.txt','w')  文件存在则打开,不存在则创建 file_obj=open('test.txt','w+') "+" 代表可读,可写 eg2: file_ob

简单文件的写操作

<?php    $keyword = "text";    $file=fopen("./back.txt","a+");    fwrite($file,$keyword);    fclose($file);?>

文件写操作

文件的写操作 1.先判断SD卡是否存在 2.多级文件存储,先判断父文件夹是否存在,不存在则创建 3.文件读写操作是耗时的,所以最好放在线程里进行. package com.exam.file; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; impo

jAVA基础 提高文件复制性能之多线程复制文件

利用IO流中的随机访问文件 RandomAccessFile 和文件通道 FileChanne 复制文件可大大提高文件的读写效率,在此基础上利用多线程复制文件使其性能更优.因线程的个数可根据文件的大小及需求而定.其大概原理是根据给定的线程个数,将文件分段,每个线程负责的数据大小=文件长度/线程个数,将不能除尽的部分留给最后一段文件所分配的线程处理.以下是实现代码及自己理解的注释,多有偏差请见谅.下面是兄弟连java培训总结的一些代码:供参考. 程序实现类代码: import java.io.Ra

linux下文件的读写操作(openreadwrite)

linux下文件的读写操作(openreadwrite) 转 http://www.2cto.com/os/201403/285837.html open(打开文件) 相关函数 read,write,fcntl,close,link,stat,umask,unlink,fopen 表头文件 #include<sys/types.h>#include<sys/stat.h>#include<fcntl.h> 定义函数 int open( const char * path

Shell的文件描述符操作,包括标准输入,标准输出,自定义文件描述符

Abstract: 1) Linux Shell 命令的标准输入.标准输出.标准错误,及其重定位: 2)Linux Shell 操作自定义文件描述符: 文件描述符是与文件相关联的一些整数,他们保持与已打开文件的关联.众所周知的文件描述符是标准输入stdin.标准输出stdout.标准错误stderr,我们可以重定位这些文件描述符关联文件的内容到另外一个文件文件描述符. 1. Linux Shell 命令的标准输入.标准输出.标准错误 当我们在编写 shell 脚本时,我们会非常频繁地操作执行命令

C++中对txt文件的读写操作

最近在做颜色校正部分的操作,发现对C++文件的读写遗忘了,特记于此:改程序是实现对txt文件的写入与读出:这是初级的写法,有时间在搞一下高级写法:注意最后别忘了close掉打开的文件! 程序如下: // test.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include<iostream> #include<fstream> #includ

python 关于文件,以及文件夹的操作

1.打开文件: f=open(r'E:\PythonProjects\test7\a.txt',mode='rt',encoding='utf-8') 以上三个单引号内分别表示:要打开的文件的路径,mode为文件打开方式具体介绍在下文,encoding为文件的字符编码,一般默认为utf-8 2.读写文件: data=f.read()  # 读文件 f.write()    # 写文件 3.关闭文件: f.close() 4.为了简便,一般采用上下文的方法进行文件操作,可不用关闭文件 1 with