Java 文件分块上传客户端和服务器端源代码

Java 文件分块上传客户端源代码  http://blog.csdn.net/defonds/article/details/8575893

MIME协议(中文版).doc

        本博客介绍如何进行文件的分块上传。本文侧重介绍客户端,服务器端请参考博客《Java 文件分块上传服务器端源代码》。建议读者朋友在阅读本文代码前先了解一下 MIME 协议。

        所谓分块上传并非把大文件进行物理分块,然后挨个上传,而是依次读取大文件的一部分文件流进行上传。分块,倒不如说分流比较切实。本文通过一个项目中的示例,说明使用 Apache 的 HttpComponents/HttpClient 对大文件进行分块上传的过程。示例使用的版本是 HttpComponents Client 4.2.1。
        本文仅以一小 demo 功能性地解释 HttpComponents/HttpClient 分块上传,没有考虑 I/O 关闭、多线程等资源因素,读者可以根据自己的项目酌情处理。
        本文核心思想及流程:以 100 MB 大小为例,大于 100 MB 的进行分块上传,否则整块上传。对于大于 100 MB 的文件,又以 100 MB 为单位进行分割,保证每次以不大于 100 MB 的大小进行上传。比如 304 MB 的一个文件会分为 100 MB、100 MB、100 MB、4 MB 等四块依次上传。第一次读取 0 字节开始的 100 MB 个字节,上传;第二次读取第 100 MB 字节开始的 100 MB 个字节,上传;第三次读取第 200 MB 字节开始的 100 MB 个字节,上传;第四次读取最后剩下的 4 MB 个字节进行上传。

        自定义的 ContentBody 源码如下,其中定义了流的读取和输出:

[html] view plaincopy
  1. package com.defonds.rtupload.common.util.block;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.io.OutputStream;  
  6. import java.io.RandomAccessFile;  
  7.   
  8. import org.apache.http.entity.mime.content.AbstractContentBody;  
  9.   
  10. import com.defonds.rtupload.GlobalConstant;  
  11.   
  12. public class BlockStreamBody extends AbstractContentBody {  
  13.       
  14.     //给MultipartEntity看的2个参数  
  15.     private long blockSize = 0;//本次分块上传的大小  
  16.     private String fileName = null;//上传文件名  
  17.     //writeTo需要的3个参数  
  18.     private int blockNumber = 0blockIndex = 0;//blockNumber分块数;blockIndex当前第几块  
  19.     private File targetFile = null;//要上传的文件  
  20.   
  21.     private BlockStreamBody(String mimeType) {  
  22.         super(mimeType);  
  23.         // TODO Auto-generated constructor stub  
  24.     }  
  25.       
  26.     /**  
  27.      * 自定义的ContentBody构造子  
  28.      * @param blockNumber分块数  
  29.      * @param blockIndex当前第几块  
  30.      * @param targetFile要上传的文件  
  31.      */  
  32.     public BlockStreamBody(int blockNumber, int blockIndex, File targetFile) {  
  33.         this("application/octet-stream");  
  34.         this.blockNumber = blockNumber;//blockNumber初始化  
  35.         this.blockIndex = blockIndex;//blockIndex初始化  
  36.         this.targetFile = targetFile;//targetFile初始化  
  37.         this.fileName = targetFile.getName();//fileName初始化  
  38.         //blockSize初始化  
  39.         if (blockIndex < blockNumber) {//不是最后一块,那就是固定大小了  
  40.             this.blockSize = GlobalConstant.CLOUD_API_LOGON_SIZE;  
  41.         } else {//最后一块  
  42.             this.blockSize = targetFile.length() - GlobalConstant.CLOUD_API_LOGON_SIZE * (blockNumber - 1);  
  43.         }  
  44.     }  
  45.   
  46.     @Override  
  47.     public void writeTo(OutputStream out) throws IOException {  
  48.         byte b[] = new byte[1024];//暂存容器  
  49.         RandomAccessFile raf  = new RandomAccessFile(targetFile, "r");//负责读取数据  
  50.         if (blockIndex == 1) {//第一块  
  51.             int n = 0;  
  52.             long readLength = 0;//记录已读字节数  
  53.             while (readLength <= blockSize - 1024) {//大部分字节在这里读取  
  54.                 n = raf.read(b, 0, 1024);  
  55.                 readLength += 1024;  
  56.                 out.write(b, 0, n);  
  57.             }  
  58.             if (readLength <= blockSize) {//余下的不足 1024 个字节在这里读取  
  59.                 n = raf.read(b, 0, (int)(blockSize - readLength));  
  60.                 out.write(b, 0, n);  
  61.             }  
  62.         } else if (blockIndex < blockNumber) {//既不是第一块,也不是最后一块  
  63.             raf.seek(GlobalConstant.CLOUD_API_LOGON_SIZE * (blockIndex - 1));//跳过前[块数*固定大小 ]个字节  
  64.             int n = 0;  
  65.             long readLength = 0;//记录已读字节数  
  66.             while (readLength <= blockSize - 1024) {//大部分字节在这里读取  
  67.                 n = raf.read(b, 0, 1024);  
  68.                 readLength += 1024;  
  69.                 out.write(b, 0, n);  
  70.             }  
  71.             if (readLength <= blockSize) {//余下的不足 1024 个字节在这里读取  
  72.                 n = raf.read(b, 0, (int)(blockSize - readLength));  
  73.                 out.write(b, 0, n);  
  74.             }  
  75.         } else {//最后一块  
  76.             raf.seek(GlobalConstant.CLOUD_API_LOGON_SIZE * (blockIndex - 1));//跳过前[块数*固定大小 ]个字节  
  77.             int n = 0;  
  78.             while ((n = raf.read(b, 0, 1024)) != -1) {  
  79.                 out.write(b, 0, n);  
  80.             }  
  81.         }  
  82.           
  83.         //TODO 最后不要忘掉关闭out/raf  
  84.     }  
  85.       
  86.     @Override  
  87.     public String getCharset() {  
  88.         // TODO Auto-generated method stub  
  89.         return null;  
  90.     }  
  91.       
  92.     @Override  
  93.     public String getTransferEncoding() {  
  94.         // TODO Auto-generated method stub  
  95.         return "binary";  
  96.     }  
  97.       
  98.     @Override  
  99.     public String getFilename() {  
  100.         // TODO Auto-generated method stub  
  101.         return fileName;  
  102.     }  
  103.   
  104.     @Override  
  105.     public long getContentLength() {  
  106.         // TODO Auto-generated method stub  
  107.         return blockSize;  
  108.     }  
  109.   
  110. }  


        在自定义的 HttpComponents/HttpClient 工具类 HttpClient4Util 里进行分块上传的封装:

[java] view plaincopy
  1. public static String restPost(String serverURL, File targetFile,Map<String, String> mediaInfoMap){  
  2.   
  3.     String content ="";  
  4.     try {  
  5.         DefaultHttpClient httpClient = new DefaultHttpClient();  
  6.         HttpPost post = new HttpPost(serverURL +"?");  
  7.         httpClient.getParams().setParameter("http.socket.timeout",60*60*1000);  
  8.         MultipartEntity mpEntity = new MultipartEntity();  
  9.         List<String> keys = new ArrayList<String>(mediaInfoMap.keySet());  
  10.         Collections.sort(keys, String.CASE_INSENSITIVE_ORDER);  
  11.         for (Iterator<String> iterator = keys.iterator(); iterator.hasNext();) {  
  12.             String key = iterator.next();  
  13.             if (StringUtils.isNotBlank(mediaInfoMap.get(key))) {  
  14.                 mpEntity.addPart(key, new StringBody(mediaInfoMap.get(key)));  
  15.             }  
  16.         }  
  17.           
  18.         if(targetFile!=null&&targetFile.exists()){  
  19.             ContentBody contentBody = new FileBody(targetFile);  
  20.             mpEntity.addPart("file", contentBody);  
  21.         }  
  22.         post.setEntity(mpEntity);  
  23.   
  24.           
  25.         HttpResponse response = httpClient.execute(post);  
  26.         content = EntityUtils.toString(response.getEntity());  
  27.         httpClient.getConnectionManager().shutdown();  
  28.     } catch (Exception e) {  
  29.         e.printStackTrace();  
  30.     }  
  31.     System.out.println("=====RequestUrl========================== "  
  32.             +getRequestUrlStrRest(serverURL, mediaInfoMap).replaceAll("&fmt=json"""));  
  33.     System.out.println("=====content========================== "+content);  
  34.     return content.trim();  
  35. }  

        其中 "file" 是分块上传服务器对分块文件参数定义的名字。细心的读者会发现,整块文件上传直接使用 Apache 官方的 InputStreamBody,而分块才使用自定义的 BlockStreamBody。

        最后调用 HttpClient4Util 进行上传:

[java] view plaincopy
  1. public static Map<String, String> uploadToDrive(  
  2.         Map<String, String> params, String domain) {  
  3.   
  4.     File targetFile = new File(params.get("filePath"));  
  5.     long targetFileSize = targetFile.length();  
  6.     int mBlockNumber = 0;  
  7.     if (targetFileSize < GlobalConstant.CLOUD_API_LOGON_SIZE) {  
  8.         mBlockNumber = 1;  
  9.     } else {  
  10.         mBlockNumber = (int) (targetFileSize / GlobalConstant.CLOUD_API_LOGON_SIZE);  
  11.         long someExtra = targetFileSize  
  12.                 % GlobalConstant.CLOUD_API_LOGON_SIZE;  
  13.         if (someExtra > 0) {  
  14.             mBlockNumber++;  
  15.         }  
  16.     }  
  17.     params.put("blockNumber", Integer.toString(mBlockNumber));  
  18.   
  19.     if (domain != null) {  
  20.         LOG.debug("Drive---domain=" + domain);  
  21.         LOG.debug("drive---url=" + "http://" + domain + "/sync"  
  22.                 + GlobalConstant.CLOUD_API_PRE_UPLOAD_PATH);  
  23.     } else {  
  24.         LOG.debug("Drive---domain=null");  
  25.     }  
  26.     String responseBodyStr = HttpClient4Util.getRest("http://" + domain  
  27.             + "/sync" + GlobalConstant.CLOUD_API_PRE_UPLOAD_PATH, params);  
  28.   
  29.     ObjectMapper mapper = new ObjectMapper();  
  30.     DrivePreInfo result;  
  31.     try {  
  32.         result = mapper.readValue(responseBodyStr, ArcDrivePreInfo.class);  
  33.     } catch (IOException e) {  
  34.         LOG.error("Drive.preUploadToArcDrive error.", e);  
  35.         throw new RtuploadException(GlobalConstant.ERROR_CODE_13001);// TODO  
  36.     }  
  37.     // JSONObject jsonObject = JSONObject.fromObject(responseBodyStr);  
  38.     if (Integer.valueOf(result.getRc()) == 0) {  
  39.         int uuid = result.getUuid();  
  40.         String upsServerUrl = result.getUploadServerUrl().replace("https",  
  41.                 "http");  
  42.         if (uuid != -1) {  
  43.             upsServerUrl = upsServerUrl  
  44.                     + GlobalConstant.CLOUD_API_UPLOAD_PATH;  
  45.             params.put("uuid", String.valueOf(uuid));  
  46.   
  47.             for (int i = 1; i <= mBlockNumber; i++) {  
  48.                 params.put("blockIndex""" + i);  
  49.                 HttpClient4Util.restPostBlock(upsServerUrl, targetFile,  
  50.                         params);//  
  51.             }  
  52.         }  
  53.     } else {  
  54.         throw new RtuploadException(GlobalConstant.ERROR_CODE_13001);// TODO  
  55.     }  
  56.     return null;  
  57. }  

        其中 params 这个 Map 里封装的是服务器分块上传所需要的一些参数,而上传块数也在这里进行确定。
        本文中的示例经本人测试能够上传大文件成功,诸如 *.mp4 的文件上传成功没有出现任何问题。如果读者朋友测试时遇到问题无法上传成功,请在博客后跟帖留言,大家共同交流下。本文示例肯定还存在很多不足之处,如果读者朋友发现还请留言指出,笔者先行谢过了。



 

Java 文件分块上传服务器端源代码 http://blog.csdn.net/defonds/article/details/8647129

 

 本博客将介绍如何进行文件的分块上传。如果读者还想了解文件的“分块”下载相关内容可以去参考博客《Java 服务器端支持断点续传的源代码【支持快车、迅雷】》。
        本文侧重介绍服务器端,客户端端请参考本篇博客的姊妹篇《Java 文件分块上传客户端源代码》,关于分块上传的思想及其流程,已在该博客中进行了详细说明,这里不再赘述。

        直接上代码。接收客户端 HTTP 分块上传请求的 Spring MVC 控制器源代码如下:

[java] view plaincopy
  1. @Controller  
  2. public class UploadController extends BaseController {  
  3.   
  4.     private static final Log log = LogFactory.getLog(UploadController.class);  
  5.     private UploadService uploadService;  
  6.     private AuthService authService;  
  7.   
  8.     /** 
  9.      * 大文件分成小文件块上传,一次传递一块,最后一块上传成功后,将合并所有已经上传的块,保存到File Server 
  10.      * 上相应的位置,并返回已经成功上传的文件的详细属性. 当最后一块上传完毕,返回上传成功的信息。此时用getFileList查询该文件, 
  11.      * 该文件的uploadStatus为2。client请自行处理该状态下文件如何显示。(for UPS Server) 
  12.      *  
  13.      */  
  14.     @RequestMapping("/core/v1/file/upload")  
  15.     @ResponseBody  
  16.     public Object upload(HttpServletResponse response,  
  17.             @RequestParam(value = "client_id", required = false) String appkey,  
  18.             @RequestParam(value = "sig", required = false) String appsig,  
  19.             @RequestParam(value = "token", required = false) String token,  
  20.             @RequestParam(value = "uuid", required = false) String uuid,  
  21.             @RequestParam(value = "block", required = false) String blockIndex,  
  22.             @RequestParam(value = "file", required = false) MultipartFile multipartFile,  
  23.             @RequestParam Map<String, String> parameters) {  
  24.   
  25.         checkEmpty(appkey, BaseException.ERROR_CODE_16002);  
  26.         checkEmpty(token, BaseException.ERROR_CODE_16007);  
  27.         checkEmpty(uuid, BaseException.ERROR_CODE_20016);  
  28.         checkEmpty(blockIndex, BaseException.ERROR_CODE_20006);  
  29.         checkEmpty(appsig, BaseException.ERROR_CODE_10010);  
  30.         if (multipartFile == null) {  
  31.             throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在  
  32.         }  
  33.         Long uuidL = parseLong(uuid, BaseException.ERROR_CODE_20016);  
  34.         Integer blockIndexI = parseInt(blockIndex, BaseException.ERROR_CODE_20006);  
  35.           
  36.         Map<String, Object> appMap = getAuthService().validateSigature(parameters);  
  37.   
  38.         AccessToken accessToken = CasUtil.checkAccessToken(token, appMap);  
  39.         Long uid = accessToken.getUid();  
  40.         String bucketUrl = accessToken.getBucketUrl();  
  41.         // 从上传目录拷贝文件到工作目录  
  42.         String fileAbsulutePath = null;  
  43.         try {  
  44.             fileAbsulutePath = this.copyFile(multipartFile.getInputStream(), multipartFile.getOriginalFilename());  
  45.         } catch (IOException ioe) {  
  46.             log.error(ioe.getMessage(), ioe);  
  47.             throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在  
  48.         }  
  49.         File uploadedFile = new File(Global.UPLOAD_TEMP_DIR + fileAbsulutePath);  
  50.         checkEmptyFile(uploadedFile);// file 非空验证  
  51.   
  52.         Object rs = uploadService.upload(uuidL, blockIndexI, uid, uploadedFile, bucketUrl);  
  53.         setHttpStatusOk(response);  
  54.         return rs;  
  55.     }  
  56.   
  57.     // TODO 查看下这里是否有问题  
  58.     // 上传文件非空验证  
  59.     private void checkEmptyFile(File file) {  
  60.         if (file == null || file.getAbsolutePath() == null) {  
  61.             throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在  
  62.         }  
  63.     }  
  64.   
  65.     /** 
  66.      * 写文件到本地文件夹 
  67.      *  
  68.      * @throws IOException 
  69.      *             返回生成的文件名 
  70.      */  
  71.     private String copyFile(InputStream inputStream, String fileName) {  
  72.         OutputStream outputStream = null;  
  73.         String tempFileName = null;  
  74.         int pointPosition = fileName.lastIndexOf(".");  
  75.         if (pointPosition < 0) {// myvedio  
  76.             tempFileName = UUID.randomUUID().toString();// 94d1d2e0-9aad-4dd8-a0f6-494b0099ff26  
  77.         } else {// myvedio.flv  
  78.             tempFileName = UUID.randomUUID() + fileName.substring(pointPosition);// 94d1d2e0-9aad-4dd8-a0f6-494b0099ff26.flv  
  79.         }  
  80.         try {  
  81.             outputStream = new FileOutputStream(Global.UPLOAD_TEMP_DIR + tempFileName);  
  82.             int readBytes = 0;  
  83.             byte[] buffer = new byte[10000];  
  84.             while ((readBytes = inputStream.read(buffer, 010000)) != -1) {  
  85.                 outputStream.write(buffer, 0, readBytes);  
  86.             }  
  87.             return tempFileName;  
  88.         } catch (IOException ioe) {  
  89.             // log.error(ioe.getMessage(), ioe);  
  90.             throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在  
  91.         } finally {  
  92.             if (outputStream != null) {  
  93.                 try {  
  94.                     outputStream.close();  
  95.                 } catch (IOException e) {  
  96.                 }  
  97.             }  
  98.             if (inputStream != null) {  
  99.                 try {  
  100.                     inputStream.close();  
  101.                 } catch (IOException e) {  
  102.                 }  
  103.             }  
  104.   
  105.         }  
  106.   
  107.     }  
  108.   
  109.     /** 
  110.      * 测试此服务是否可用 
  111.      *  
  112.      * @param response 
  113.      * @return 
  114.      * @author zwq7978 
  115.      */  
  116.     @RequestMapping("/core/v1/file/testServer")  
  117.     @ResponseBody  
  118.     public Object testServer(HttpServletResponse response) {  
  119.         setHttpStatusOk(response);  
  120.         return Global.SUCCESS_RESPONSE;  
  121.     }  
  122.   
  123.     public UploadService getUploadService() {  
  124.         return uploadService;  
  125.     }  
  126.   
  127.     public void setUploadService(UploadService uploadService) {  
  128.         this.uploadService = uploadService;  
  129.     }  
  130.   
  131.     public void setAuthService(AuthService authService) {  
  132.         this.authService = authService;  
  133.     }  
  134.   
  135.     public AuthService getAuthService() {  
  136.         return authService;  
  137.     }  
  138.   
  139. }  

        比如要上传的文件是 test450k.mp4。对照《Java 文件分块上传客户端源代码》中分块上传服务器对分块文件参数定义的名字"file",upload 方法里使用的是 MultipartFile 接收该对象。对于每次的 HTTP 请求,使用 copyFile 方法将文件流输出到服务器本地的一个临时文件夹里,比如作者的是 D:/defonds/syncPath/uploadTemp,该文件下会有 50127019-b63b-4a54-8f53-14efd1e58ada.mp4 临时文件生成用于保存上传文件流。

        分块依次上传。当所有块都上传完毕之后,将这些临时文件都转移到服务器指定目录中,比如作者的这个目录是 D:/defonds/syncPath/file,在该文件夹下会有/1/temp_dir_5_1 目录生成,而 uploadTemp 的临时文件则被挨个转移到这个文件夹下,生成形如 5.part0001 的文件。以下是文件转移的源代码:

[java] view plaincopy
  1. /** 
  2.  * 把所有块从临时文件目录移到指定本地目录或S2/S3 
  3.  *  
  4.  * @param preUpload 
  5.  */  
  6. private void moveBlockFiles(BlockPreuploadFileInfo preUpload) {  
  7.     @SuppressWarnings("unchecked")  
  8.     String[] s3BlockUrl=new String[preUpload.getBlockNumber()];  
  9.     String[] localBlockUrl=new String[preUpload.getBlockNumber()];//本地的块文件路径    以便以后删除  
  10.     List<BlockUploadInfo> blocks = (List<BlockUploadInfo>) getBaseDao().queryForList(  
  11.             "upload.getBlockUploadFileByUuid", preUpload.getUuid());  
  12.               
  13.     String tempDirName = SyncUtil.getTempDirName(preUpload.getUuid(), preUpload.getUid());  
  14.     String parentPath = Global.UPLOAD_ABSOLUTE_PAHT_ + Global.PATH_SEPARATIVE_SIGN  
  15.             + String.valueOf(preUpload.getUid());  
  16.     String dirPath = parentPath + Global.PATH_SEPARATIVE_SIGN + tempDirName;  
  17.     new File(dirPath).mkdirs();//创建存放块文件的文件夹 (本地)  
  18.     int j=0;  
  19.     for (BlockUploadInfo info : blocks) {  
  20.         try {  
  21.             String strBlockIndex = createStrBlockIndex(info.getBlockIndex());  
  22.             String suffixPath = preUpload.getUuid() + ".part" + strBlockIndex;  
  23.             String tempFilePath = info.getTempFile();  
  24.             File tempFile = new File(tempFilePath);  
  25.             File tmpFile = new File(dirPath + suffixPath);  
  26.             if (tmpFile.exists()) {  
  27.                 FileUtils.deleteQuietly(tmpFile);  
  28.             }  
  29.             FileUtils.moveFile(tempFile, tmpFile);  
  30.             localBlockUrl[j]=dirPath + suffixPath;  
  31.             j++;  
  32.             info.setStatus(Global.MOVED_TO_NEWDIR);  
  33.             getBaseDao().update("upload.updateBlockUpload", info);  
  34.             if (log.isInfoEnabled())  
  35.                 log.info(preUpload.getUuid() + " " + info.getBuId() + " moveBlockFiles");  
  36.         } catch (IOException e) {  
  37.             log.error(e.getMessage(), e);  
  38.             throw new BaseException("file not found");  
  39.         }  
  40.     }  
  41.     preUpload.setLocalBlockUrl(localBlockUrl);  
  42.     preUpload.setDirPath(dirPath);  
  43.     preUpload.setStatus(Global.MOVED_TO_NEWDIR);  
  44.     getBaseDao().update("upload.updatePreUploadInfo", preUpload);  
  45. }  
  46.   
  47. private String createStrBlockIndex(int blockIndex) {  
  48.     String strBlockIndex;  
  49.     if (blockIndex < 10) {  
  50.         strBlockIndex = "000" + blockIndex;  
  51.     } else if (10 <= blockIndex && blockIndex < 100) {  
  52.         strBlockIndex = "00" + blockIndex;  
  53.     } else if (100 <= blockIndex && blockIndex < 1000) {  
  54.         strBlockIndex = "0" + blockIndex;  
  55.     } else {  
  56.         strBlockIndex = "" + blockIndex;  
  57.     }  
  58.     return strBlockIndex;  
  59. }  

        最后是文件的组装源代码:

[java] view plaincopy
  1. /** 
  2.  * 组装文件 
  3.  *  
  4.  */  
  5. private void assembleFileWithBlock(BlockPreuploadFileInfo preUpload) {  
  6.     String dirPath = preUpload.getDirPath();  
  7.     // 开始在指定目录组装文件  
  8.     String uploadedUrl = null;  
  9.     String[] separatedFiles;  
  10.     String[][] separatedFilesAndSize;  
  11.     int fileNum = 0;  
  12.     File file = new File(dirPath);  
  13.     separatedFiles = file.list();  
  14.     separatedFilesAndSize = new String[separatedFiles.length][2];  
  15.     Arrays.sort(separatedFiles);  
  16.     fileNum = separatedFiles.length;  
  17.     for (int i = 0; i < fileNum; i++) {  
  18.         separatedFilesAndSize[i][0] = separatedFiles[i];  
  19.         String fileName = dirPath + separatedFiles[i];  
  20.         File tmpFile = new File(fileName);  
  21.         long fileSize = tmpFile.length();  
  22.         separatedFilesAndSize[i][1] = String.valueOf(fileSize);  
  23.     }  
  24.   
  25.     RandomAccessFile fileReader = null;  
  26.     RandomAccessFile fileWrite = null;  
  27.     long alreadyWrite = 0;  
  28.     int len = 0;  
  29.     byte[] buf = new byte[1024];  
  30.     try {  
  31.         uploadedUrl = Global.UPLOAD_ABSOLUTE_PAHT_ + Global.PATH_SEPARATIVE_SIGN + preUpload.getUid() + Global.PATH_SEPARATIVE_SIGN + preUpload.getUuid();  
  32.         fileWrite = new RandomAccessFile(uploadedUrl, "rw");  
  33.         for (int i = 0; i < fileNum; i++) {  
  34.             fileWrite.seek(alreadyWrite);  
  35.             // 读取  
  36.             fileReader = new RandomAccessFile((dirPath + separatedFilesAndSize[i][0]), "r");  
  37.             // 写入  
  38.             while ((len = fileReader.read(buf)) != -1) {  
  39.                 fileWrite.write(buf, 0, len);  
  40.             }  
  41.             fileReader.close();  
  42.             alreadyWrite += Long.parseLong(separatedFilesAndSize[i][1]);  
  43.         }  
  44.         fileWrite.close();  
  45.         preUpload.setStatus(Global.ASSEMBLED);  
  46.         preUpload.setServerPath(uploadedUrl);  
  47.         getBaseDao().update("upload.updatePreUploadInfo", preUpload);  
  48.           
  49.         if(Global.BLOCK_UPLOAD_TO!=Global.BLOCK_UPLOAD_TO_LOCAL)  
  50.         {  
  51.         //组装完毕没有问题  删除掉S2/S3上的block  
  52.         String[] path=preUpload.getS3BlockUrl();  
  53.         for (String string : path) {  
  54.             try {  
  55.                 if(Global.BLOCK_UPLOAD_TO==Global.BLOCK_UPLOAD_TO_S2)  
  56.                 {  
  57.                 S2Util.deleteFile(preUpload.getBucketUrl(), string);  
  58.                 }else  
  59.                 {  
  60.                 S3Util.deleteFile(preUpload.getBucketUrl(), string);  
  61.                 }  
  62.             } catch (Exception e) {  
  63.                 log.error(e.getMessage(), e);  
  64.             }  
  65.         }  
  66.         }  
  67.         if (log.isInfoEnabled())  
  68.             log.info(preUpload.getUuid() + " assembleFileWithBlock");  
  69.     } catch (IOException e) {  
  70.         log.error(e.getMessage(), e);  
  71.         try {  
  72.             if (fileReader != null) {  
  73.                 fileReader.close();  
  74.             }  
  75.             if (fileWrite != null) {  
  76.                 fileWrite.close();  
  77.             }  
  78.         } catch (IOException ex) {  
  79.             log.error(e.getMessage(), e);  
  80.         }  
  81.     }  
  82.   
  83. }  

        BlockPreuploadFileInfo 是我们自定义的业务文件处理 bean。
        OK,分块上传的服务器、客户端源代码及其工作流程至此已全部介绍完毕,以上源代码全部是经过项目实践过的,大部分现在仍运行于一些项目之中。有兴趣的朋友可以自己动手,将以上代码自行改造,看看能否运行成功。如果遇到问题可以在本博客下跟帖留言,大家一起讨论讨论。

原文地址:https://www.cnblogs.com/hzcya1995/p/13318244.html