java 文件移動 常規四種方法 和利用FileCopyUtils.copy( , )方法

使用 java 進行文件拷貝 相信很多人都會用,,不過效率上是否最好呢?
最近看了看NIO決定試一試 java  NIO 到底有什麼性能的提升.

第一種方法:古老的方式

 public static long forJava(File f1,File f2) throws Exception{
  long time=new Date().getTime();
  int length=2097152;
  FileInputStream in=new FileInputStream(f1);
  FileOutputStream out=new FileOutputStream(f2);
  byte[] buffer=new byte[length];
  while(true){
   int ins=in.read(buffer);
   if(ins==-1){
    in.close();
    out.flush();
    out.close();
    return new Date().getTime()-time;
   }else
    out.write(buffer,0,ins);
  }
 }

方法的2參數分別是原始文件,和拷貝的目的文件.這裏不做過多介紹.

實現方法很簡單,分別對2個文件構建輸入輸出流,並且使用一個字節數組作爲我們內存的緩存器, 然後使用流從f1 中讀出數據到緩存裏,在將緩存數據寫到f2裏面去.這裏的緩存是2MB的字節數組

第2種方法:使用NIO中的管道到管道傳輸

    public static long forTransfer(File f1,File f2) throws Exception{
        long time=new Date().getTime();
        int length=2097152;
        FileInputStream in=new FileInputStream(f1);
        FileOutputStream out=new FileOutputStream(f2);
        FileChannel inC=in.getChannel();
        FileChannel outC=out.getChannel();
        int i=0;
        while(true){
            if(inC.position()==inC.size()){
                inC.close();
                outC.close();
                return new Date().getTime()-time;
            }
            if((inC.size()-inC.position())<20971520)
                length=(int)(inC.size()-inC.position());
            else
                length=20971520;
            inC.transferTo(inC.position(),length,outC);
            inC.position(inC.position()+length);
            i++;
        }
    }

實現方法:在第一種實現方法基礎上對輸入輸出流獲得其管道,然後分批次的從f1的管道中像f2的管道中輸入數據每次輸入的數據最大爲2MB

方法3:內存文件景象寫(讀文件沒有使用文件景象,有興趣的可以回去試試,,我就不試了,估計會更快)

    public static long forImage(File f1,File f2) throws Exception{
        long time=new Date().getTime();
        int length=2097152;
        FileInputStream in=new FileInputStream(f1);
        RandomAccessFile out=new RandomAccessFile(f2,"rw");
        FileChannel inC=in.getChannel();
        MappedByteBuffer outC=null;
        MappedByteBuffer inbuffer=null;
        byte[] b=new byte[length];
        while(true){
            if(inC.position()==inC.size()){
                inC.close();
                outC.force();
                out.close();
                return new Date().getTime()-time;
            }
            if((inC.size()-inC.position())<length){
                length=(int)(inC.size()-inC.position());
            }else{
                length=20971520;
            }
            b=new byte[length];
            inbuffer=inC.map(MapMode.READ_ONLY,inC.position(),length);
            inbuffer.load();
            inbuffer.get(b);
            outC=out.getChannel().map(MapMode.READ_WRITE,inC.position(),length);
            inC.position(b.length+inC.position());
            outC.put(b);
            outC.force();
        }
    }

實現方法:跟傷2個例子不一樣,這裏寫文件流沒有使用管道而是使用內存文件映射(假設文件f2在內存中).在循環中從f1的管道中讀取數據到字節數組裏,然後在像內存映射的f2文件中寫數據.

第4種方法:管道對管道

    public static long forChannel(File f1,File f2) throws Exception{
        long time=new Date().getTime();
        int length=2097152;
        FileInputStream in=new FileInputStream(f1);
        FileOutputStream out=new FileOutputStream(f2);
        FileChannel inC=in.getChannel();
        FileChannel outC=out.getChannel();
        ByteBuffer b=null;
        while(true){
            if(inC.position()==inC.size()){
                inC.close();
                outC.close();
                return new Date().getTime()-time;
            }
            if((inC.size()-inC.position())<length){
                length=(int)(inC.size()-inC.position());
            }else
                length=2097152;
            b=ByteBuffer.allocateDirect(length);
            inC.read(b);
            b.flip();
            outC.write(b);
            outC.force(false);
        }
    }

這裏實現方式與第3種實現方式很類似,不過沒有使用內存影射.

 

下面是對49.3MB的文件進行拷貝的測試時間(毫秒)

Start Copy File...  file size:50290KB
CopyFile:b1.rmvb mode:forChannel  RunTime:3203
CopyFile:b1.rmvb mode:forImage  RunTime:3328
CopyFile:b1.rmvb mode:forJava  RunTime:2172
CopyFile:b1.rmvb mode:forTransfer RunTime:1406
End Copy File!





PS:這個一個上傳方法 用的uploadify插件

public ReturnObject uploadFile(String deleteFileName,
            HttpServletRequest request,HttpServletResponse response)
            throws IOException{
        System.out.println("附件上傳sessionInfo:"
                + ContextUtils.getSession().getId());
        String basePath = Global.BASE_UPLOAD_FOLDER; // 獲取基礎路徑
        String TemporaryFile=Global.BASE_UPLOAD_TEMPORARYFILE;//獲取臨時文件夾 
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
        Map<String,MultipartFile> fileMap = multipartRequest.getFileMap();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
        SimpleDateFormat sp = new SimpleDateFormat("yyyyMMdd");
        // TODO multi-tenant.id 目前租戶ID都是707 待以後完善後multi-tenant.id 就是租戶ID
        String ctxPath = basePath + File.separator + 707 + File.separator
                + ContextUtils.getCurrentSecurityUser().getUsername()
                + File.separator+TemporaryFile+File.separator;// 完整路徑
        String ctxPathView = sp.format(new Date()) + "/";// 用於在數據庫存儲的路徑-從文件夾開始
        String ymd = sdf.format(new Date());
        // 如果不存在,創建文件夾
        File file = new File(ctxPath);
        if (!file.exists()){
            file.mkdirs();
        }
        ReturnObject ro = new ReturnObject();
        String fileNames = "";
        for (Map.Entry<String,MultipartFile> entity : fileMap.entrySet()){
            MultipartFile mf = entity.getValue();
            String fileFullname = mf.getOriginalFilename();
            // String fileName = mf.getOriginalFilename();
            String fileName = ymd + "_" + fileFullname;
            File uploadFile = new File(ctxPath + fileName);
            try{
                FileCopyUtils.copy(mf.getBytes(),uploadFile); //可以利用此方法進行文件移動,通過流直接移動
                // copy成功之後保存上傳數據記錄
                UploadFile uploadBean = new UploadFile();
                uploadBean.setUpdUser(ContextUtils.getCurrentSecurityUser()
                        .getUsername());
                uploadBean.setOriginName(fileFullname);
                uploadBean.setFileName(fileName);
                uploadBean.setUpdTime(new Date());
                uploadBean.setIsFunction("other");
                if (uploadBean != null){
                    uploadFileService.save(uploadBean);
                }
                fileNames += fileName + ",";
            }
            catch (IOException e){
                e.printStackTrace();
                ro.setSuccess(false);
                ro.setMsg(e.getMessage());
                return ro;
            }
        }
        if (fileNames.endsWith(",")){
            fileNames = fileNames.substring(0,fileNames.length() - 1);
        }
        ro.setSuccess(true);
        ro.setMsg("上傳文件成功");
        HashMap<String,Object> hsfiled = new HashMap<String,Object>();
        hsfiled.put("fileNames",fileNames);
        hsfiled.put("filepatch",ctxPathView);// 數據庫保存路徑(從時間開始截取的)
        ro.setObj(hsfiled);
        // 清空不需要的歷史文件
        try{
            log.info("刪除不需要的文件:" + deleteFileName);
            String ctPath = basePath + File.separator + File.separator
                    + deleteFileName;
            // 如果不存在,創建文件夾
            if (deleteFileName != null){
                File fileTobeDelete = new File(ctPath);
                if (fileTobeDelete.exists()){
                    fileTobeDelete.delete();
                }
            }
        }
        catch (Exception e){
            log.error("刪除歷史文件出錯:" + e.getMessage());
        }
        return ro;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章