Java分块上传文件_Java 文件分块上传服务器端源代码

本文侧重介绍服务器端,客户端端请参考本篇博客的姊妹篇《 Java 文件分块上传客户端源代码 》,关于分块上传的思想及其流程,已在该博客中进行了详细说明,这里不再赘述。

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

@Controller

public class UploadController extends BaseController {

private static final Log log = LogFactory.getLog(UploadController.class);

private UploadService uploadService;

private AuthService authService;

/**

* 大文件分成小文件块上传,一次传递一块,最后一块上传成功后,将合并所有已经上传的块,保存到File Server

* 上相应的位置,并返回已经成功上传的文件的详细属性. 当最后一块上传完毕,返回上传成功的信息。此时用getFileList查询该文件,

* 该文件的uploadStatus为2。client请自行处理该状态下文件如何显示。(for UPS Server)

*

*/

@RequestMapping("/core/v1/file/upload")

@ResponseBody

public Object upload(HttpServletResponse response,

@RequestParam(value = "client_id", required = false) String appkey,

@RequestParam(value = "sig", required = false) String appsig,

@RequestParam(value = "token", required = false) String token,

@RequestParam(value = "uuid", required = false) String uuid,

@RequestParam(value = "block", required = false) String blockIndex,

@RequestParam(value = "file", required = false) MultipartFile multipartFile,

@RequestParam Map parameters) {

checkEmpty(appkey, BaseException.ERROR_CODE_16002);

checkEmpty(token, BaseException.ERROR_CODE_16007);

checkEmpty(uuid, BaseException.ERROR_CODE_20016);

checkEmpty(blockIndex, BaseException.ERROR_CODE_20006);

checkEmpty(appsig, BaseException.ERROR_CODE_10010);

if (multipartFile == null) {

throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在

}

Long uuidL = parseLong(uuid, BaseException.ERROR_CODE_20016);

Integer blockIndexI = parseInt(blockIndex, BaseException.ERROR_CODE_20006);

Map appMap = getAuthService().validateSigature(parameters);

AccessToken accessToken = CasUtil.checkAccessToken(token, appMap);

Long uid = accessToken.getUid();

String bucketUrl = accessToken.getBucketUrl();

// 从上传目录拷贝文件到工作目录

String fileAbsulutePath = null;

try {

fileAbsulutePath = this.copyFile(multipartFile.getInputStream(), multipartFile.getOriginalFilename());

} catch (IOException ioe) {

log.error(ioe.getMessage(), ioe);

throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在

}

File uploadedFile = new File(Global.UPLOAD_TEMP_DIR + fileAbsulutePath);

checkEmptyFile(uploadedFile);// file 非空验证

Object rs = uploadService.upload(uuidL, blockIndexI, uid, uploadedFile, bucketUrl);

setHttpStatusOk(response);

return rs;

}

// TODO 查看下这里是否有问题

// 上传文件非空验证

private void checkEmptyFile(File file) {

if (file == null || file.getAbsolutePath() == null) {

throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在

}

}

/**

* 写文件到本地文件夹

*

* @throws IOException

* 返回生成的文件名

*/

private String copyFile(InputStream inputStream, String fileName) {

OutputStream outputStream = null;

String tempFileName = null;

int pointPosition = fileName.lastIndexOf(".");

if (pointPosition < 0) {// myvedio

tempFileName = UUID.randomUUID().toString();// 94d1d2e0-9aad-4dd8-a0f6-494b0099ff26

} else {// myvedio.flv

tempFileName = UUID.randomUUID() + fileName.substring(pointPosition);// 94d1d2e0-9aad-4dd8-a0f6-494b0099ff26.flv

}

try {

outputStream = new FileOutputStream(Global.UPLOAD_TEMP_DIR + tempFileName);

int readBytes = 0;

byte[] buffer = new byte[10000];

while ((readBytes = inputStream.read(buffer, 0, 10000)) != -1) {

outputStream.write(buffer, 0, readBytes);

}

return tempFileName;

} catch (IOException ioe) {

// log.error(ioe.getMessage(), ioe);

throw new BaseException(BaseException.ERROR_CODE_20020);// 上传文件不存在

} finally {

if (outputStream != null) {

try {

outputStream.close();

} catch (IOException e) {

}

}

if (inputStream != null) {

try {

inputStream.close();

} catch (IOException e) {

}

}

}

}

/**

* 测试此服务是否可用

*

* @param response

* @return

* @author zwq7978

*/

@RequestMapping("/core/v1/file/testServer")

@ResponseBody

public Object testServer(HttpServletResponse response) {

setHttpStatusOk(response);

return Global.SUCCESS_RESPONSE;

}

public UploadService getUploadService() {

return uploadService;

}

public void setUploadService(UploadService uploadService) {

this.uploadService = uploadService;

}

public void setAuthService(AuthService authService) {

this.authService = authService;

}

public AuthService getAuthService() {

return authService;

}

}

比如要上传的文件是 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 的文件。以下是文件转移的源代码:

/**

* 把所有块从临时文件目录移到指定本地目录或S2/S3

*

* @param preUpload

*/

private void moveBlockFiles(BlockPreuploadFileInfo preUpload) {

@SuppressWarnings("unchecked")

String[] s3BlockUrl=new String[preUpload.getBlockNumber()];

String[] localBlockUrl=new String[preUpload.getBlockNumber()];//本地的块文件路径 以便以后删除

List blocks = (List) getBaseDao().queryForList(

"upload.getBlockUploadFileByUuid", preUpload.getUuid());

String tempDirName = SyncUtil.getTempDirName(preUpload.getUuid(), preUpload.getUid());

String parentPath = Global.UPLOAD_ABSOLUTE_PAHT_ + Global.PATH_SEPARATIVE_SIGN

+ String.valueOf(preUpload.getUid());

String dirPath = parentPath + Global.PATH_SEPARATIVE_SIGN + tempDirName;

new File(dirPath).mkdirs();//创建存放块文件的文件夹 (本地)

int j=0;

for (BlockUploadInfo info : blocks) {

try {

String strBlockIndex = createStrBlockIndex(info.getBlockIndex());

String suffixPath = preUpload.getUuid() + ".part" + strBlockIndex;

String tempFilePath = info.getTempFile();

File tempFile = new File(tempFilePath);

File tmpFile = new File(dirPath + suffixPath);

if (tmpFile.exists()) {

FileUtils.deleteQuietly(tmpFile);

}

FileUtils.moveFile(tempFile, tmpFile);

localBlockUrl[j]=dirPath + suffixPath;

j++;

info.setStatus(Global.MOVED_TO_NEWDIR);

getBaseDao().update("upload.updateBlockUpload", info);

if (log.isInfoEnabled())

log.info(preUpload.getUuid() + " " + info.getBuId() + " moveBlockFiles");

} catch (IOException e) {

log.error(e.getMessage(), e);

throw new BaseException("file not found");

}

}

preUpload.setLocalBlockUrl(localBlockUrl);

preUpload.setDirPath(dirPath);

preUpload.setStatus(Global.MOVED_TO_NEWDIR);

getBaseDao().update("upload.updatePreUploadInfo", preUpload);

}

private String createStrBlockIndex(int blockIndex) {

String strBlockIndex;

if (blockIndex < 10) {

strBlockIndex = "000" + blockIndex;

} else if (10 <= blockIndex && blockIndex < 100) {

strBlockIndex = "00" + blockIndex;

} else if (100 <= blockIndex && blockIndex < 1000) {

strBlockIndex = "0" + blockIndex;

} else {

strBlockIndex = "" + blockIndex;

}

return strBlockIndex;

}

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

/**

* 组装文件

*

*/

private void assembleFileWithBlock(BlockPreuploadFileInfo preUpload) {

String dirPath = preUpload.getDirPath();

// 开始在指定目录组装文件

String uploadedUrl = null;

String[] separatedFiles;

String[][] separatedFilesAndSize;

int fileNum = 0;

File file = new File(dirPath);

separatedFiles = file.list();

separatedFilesAndSize = new String[separatedFiles.length][2];

Arrays.sort(separatedFiles);

fileNum = separatedFiles.length;

for (int i = 0; i < fileNum; i++) {

separatedFilesAndSize[i][0] = separatedFiles[i];

String fileName = dirPath + separatedFiles[i];

File tmpFile = new File(fileName);

long fileSize = tmpFile.length();

separatedFilesAndSize[i][1] = String.valueOf(fileSize);

}

RandomAccessFile fileReader = null;

RandomAccessFile fileWrite = null;

long alreadyWrite = 0;

int len = 0;

byte[] buf = new byte[1024];

try {

uploadedUrl = Global.UPLOAD_ABSOLUTE_PAHT_ + Global.PATH_SEPARATIVE_SIGN + preUpload.getUid() + Global.PATH_SEPARATIVE_SIGN + preUpload.getUuid();

fileWrite = new RandomAccessFile(uploadedUrl, "rw");

for (int i = 0; i < fileNum; i++) {

fileWrite.seek(alreadyWrite);

// 读取

fileReader = new RandomAccessFile((dirPath + separatedFilesAndSize[i][0]), "r");

// 写入

while ((len = fileReader.read(buf)) != -1) {

fileWrite.write(buf, 0, len);

}

fileReader.close();

alreadyWrite += Long.parseLong(separatedFilesAndSize[i][1]);

}

fileWrite.close();

preUpload.setStatus(Global.ASSEMBLED);

preUpload.setServerPath(uploadedUrl);

getBaseDao().update("upload.updatePreUploadInfo", preUpload);

if(Global.BLOCK_UPLOAD_TO!=Global.BLOCK_UPLOAD_TO_LOCAL)

{

//组装完毕没有问题 删除掉S2/S3上的block

String[] path=preUpload.getS3BlockUrl();

for (String string : path) {

try {

if(Global.BLOCK_UPLOAD_TO==Global.BLOCK_UPLOAD_TO_S2)

{

S2Util.deleteFile(preUpload.getBucketUrl(), string);

}else

{

S3Util.deleteFile(preUpload.getBucketUrl(), string);

}

} catch (Exception e) {

log.error(e.getMessage(), e);

}

}

}

if (log.isInfoEnabled())

log.info(preUpload.getUuid() + " assembleFileWithBlock");

} catch (IOException e) {

log.error(e.getMessage(), e);

try {

if (fileReader != null) {

fileReader.close();

}

if (fileWrite != null) {

fileWrite.close();

}

} catch (IOException ex) {

log.error(e.getMessage(), e);

}

}

}

BlockPreuploadFileInfo 是我们自定义的业务文件处理 bean。

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值