直接上代码接收客户端 HTTP 分块上传请求的 Spring MVC 控制器源代码如下
[java]
@Controller
public class UploadController extends BaseController {
private static final Log log = LogFactorygetLog(UploadControllerclass)
private UploadService uploadService;
private AuthService authService;
/**
* 大文件分成小文件块上传一次传递一块最后一块上传成功后将合并所有已经上传的块保存到File Server
* 上相应的位置并返回已经成功上传的文件的详细属性 当最后一块上传完毕返回上传成功的信息此时用getFileList查询该文件
* 该文件的uploadStatus为client请自行处理该状态下文件如何显示(for UPS Server)
*
*/
@RequestMapping(/core/v/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<String String> parameters) {
checkEmpty(appkey BaseExceptionERROR_CODE_)
checkEmpty(token BaseExceptionERROR_CODE_)
checkEmpty(uuid BaseExceptionERROR_CODE_)
checkEmpty(blockIndex BaseExceptionERROR_CODE_)
checkEmpty(appsig BaseExceptionERROR_CODE_)
if (multipartFile == null) {
throw new BaseException(BaseExceptionERROR_CODE_)// 上传文件不存在
}
Long uuidL = parseLong(uuid BaseExceptionERROR_CODE_)
Integer blockIndexI = parseInt(blockIndex BaseExceptionERROR_CODE_)
Map<String Object> appMap = getAuthService()validateSigature(parameters)
AccessToken accessToken = CasUtilcheckAccessToken(token appMap)
Long uid = accessTokengetUid()
String bucketUrl = accessTokengetBucketUrl()
// 从上传目录拷贝文件到工作目录
String fileAbsulutePath = null;
try {
fileAbsulutePath = pyFile(multipartFilegetInputStream() multipartFilegetOriginalFilename())
} catch (IOException ioe) {
logerror(ioegetMessage() ioe)
throw new BaseException(BaseExceptionERROR_CODE_)// 上传文件不存在
}
File uploadedFile = new File(GlobalUPLOAD_TEMP_DIR + fileAbsulutePath)
checkEmptyFile(uploadedFile)// file 非空验证
Object rs = uploadServiceupload(uuidL blockIndexI uid uploadedFile bucketUrl)
setHttpStatusOk(response)
return rs;
}
// TODO 查看下这里是否有问题
// 上传文件非空验证
private void checkEmptyFile(File file) {
if (file == null || filegetAbsolutePath() == null) {
throw new BaseException(BaseExceptionERROR_CODE_)// 上传文件不存在
}
}
/**
* 写文件到本地文件夹
*
* @throws IOException
* 返回生成的文件名
*/
private String copyFile(InputStream inputStream String fileName) {
OutputStream outputStream = null;
String tempFileName = null;
int pointPosition = fileNamelastIndexOf()
if (pointPosition < ) {// myvedio
tempFileName = UUIDrandomUUID()toString()// ddeaadddafbff
} else {// myvedioflv
tempFileName = UUIDrandomUUID() + fileNamesubstring(pointPosition)// ddeaadddafbffflv
}
try {
outputStream = new FileOutputStream(GlobalUPLOAD_TEMP_DIR + tempFileName)
int readBytes = ;
byte[] buffer = new byte[];
while ((readBytes = inputStreamread(buffer )) != ) {
outputStreamwrite(buffer readBytes)
}
return tempFileName;
} catch (IOException ioe) {
// logerror(ioegetMessage() ioe)
throw new BaseException(BaseExceptionERROR_CODE_)// 上传文件不存在
} finally {
if (outputStream != null) {
try {
outputStreamclose()
} catch (IOException e) {
}
}
if (inputStream != null) {
try {
inputStreamclose()
} catch (IOException e) {
}
}
}
}
/**
* 测试此服务是否可用
*
* @param response
* @return
* @author zwq
*/
@RequestMapping(/core/v/file/testServer)
@ResponseBody
public Object testServer(HttpServletResponse response) {
setHttpStatusOk(response)
return GlobalSUCCESS_RESPONSE;
}
public UploadService getUploadService() {
return uploadService;
}
public void setUploadService(UploadService uploadService) {
thisuploadService = uploadService;
}
public void setAuthService(AuthService authService) {
thisauthService = authService;
}
public AuthService getAuthService() {
return authService;
}
}
比如要上传的文件是 testkmp对照《Java 文件分块上传客户端源代码》中分块上传服务器对分块文件参数定义的名字fileupload 方法里使用的是 MultipartFile 接收该对象对于每次的 HTTP 请求使用 copyFile 方法将文件流输出到服务器本地的一个临时文件夹里比如作者的是 D:/defonds/syncPath/uploadTemp该文件下会有 bbafefdeadamp 临时文件生成用于保存上传文件流
分块依次上传当所有块都上传完毕之后将这些临时文件都转移到服务器指定目录中比如作者的这个目录是 D:/defonds/syncPath/file在该文件夹下会有//temp_dir__ 目录生成而 uploadTemp 的临时文件则被挨个转移到这个文件夹下生成形如 part 的文件以下是文件转移的源代码
[java]
/**
* 把所有块从临时文件目录移到指定本地目录或S/S
*
* @param preUpload
*/
private void moveBlockFiles(BlockPreuploadFileInfo preUpload) {
@SuppressWarnings(unchecked)
String[] sBlockUrl=new String[preUploadgetBlockNumber()];
String[] localBlockUrl=new String[preUploadgetBlockNumber()];//本地的块文件路径 以便以后删除
List<BlockUploadInfo> blocks = (List<BlockUploadInfo>) getBaseDao()queryForList(
uploadgetBlockUploadFileByUuid preUploadgetUuid())
String tempDirName = SyncUtilgetTempDirName(preUploadgetUuid() preUploadgetUid())
String parentPath = GlobalUPLOAD_ABSOLUTE_PAHT_ + GlobalPATH_SEPARATIVE_SIGN
+ StringvalueOf(preUploadgetUid())
String dirPath = parentPath + GlobalPATH_SEPARATIVE_SIGN + tempDirName;
new File(dirPath)mkdirs()//创建存放块文件的文件夹 (本地)
int j=;
for (BlockUploadInfo info : blocks) {
try {
String strBlockIndex = createStrBlockIndex(infogetBlockIndex())
String suffixPath = preUploadgetUuid() + part + strBlockIndex;
String tempFilePath = infogetTempFile()
File tempFile = new File(tempFilePath)
File tmpFile = new File(dirPath + suffixPath)
if (tmpFileexists()) {
FileUtilsdeleteQuietly(tmpFile)
}
FileUtilsmoveFile(tempFile tmpFile)
localBlockUrl[j]=dirPath + suffixPath;
j++;
infosetStatus(GlobalMOVED_TO_NEWDIR)
getBaseDao()update(uploadupdateBlockUpload info)
if (logisInfoEnabled())
(preUploadgetUuid() + + infogetBuId() + moveBlockFiles)
} catch (IOException e) {
logerror(egetMessage() e)
throw new BaseException(file not found)
}
}
preUploadsetLocalBlockUrl(localBlockUrl)
preUploadsetDirPath(dirPath)
preUploadsetStatus(GlobalMOVED_TO_NEWDIR)
getBaseDao()update(uploadupdatePreUploadInfo preUpload)
}
private String createStrBlockIndex(int blockIndex) {
String strBlockIndex;
if (blockIndex < ) {
strBlockIndex = + blockIndex;
} else if ( <= blockIndex && blockIndex < ) {
strBlockIndex = + blockIndex;
} else if ( <= blockIndex && blockIndex < ) {
strBlockIndex = + blockIndex;
} else {
strBlockIndex = + blockIndex;
}
return strBlockIndex;
}
最后是文件的组装源代码
[java]
/**
* 组装文件
*
*/
private void assembleFileWithBlock(BlockPreuploadFileInfo preUpload) {
String dirPath = preUploadgetDirPath()
// 开始在指定目录组装文件
String uploadedUrl = null;
String[] separatedFiles;
String[][] separatedFilesAndSize;
int fileNum = ;
File file = new File(dirPath)
separatedFiles = filelist()
separatedFilesAndSize = new String[separatedFileslength][];
Arrayssort(separatedFiles)
fileNum = separatedFileslength;
for (int i = ; i < fileNum; i++) {
separatedFilesAndSize[i][] = separatedFiles[i];
String fileName = dirPath + separatedFiles[i];
File tmpFile = new File(fileName)
long fileSize = tmpFilelength()
separatedFilesAndSize[i][] = StringvalueOf(fileSize)
}
RandomAccessFile fileReader = null;
RandomAccessFile fileWrite = null;
long alreadyWrite = ;
int len = ;
byte[] buf = new byte[];
try {
uploadedUrl = GlobalUPLOAD_ABSOLUTE_PAHT_ + GlobalPATH_SEPARATIVE_SIGN + preUploadgetUid() + GlobalPATH_SEPARATIVE_SIGN + preUploadgetUuid()
fileWrite = new RandomAccessFile(uploadedUrl rw)
for (int i = ; i < fileNum; i++) {
fileWriteseek(alreadyWrite)
// 读取
fileReader = new RandomAccessFile((dirPath + separatedFilesAndSize[i][]) r)
// 写入
while ((len = fileReaderread(buf)) != ) {
fileWritewrite(buf len)
}
fileReaderclose()
alreadyWrite += LongparseLong(separatedFilesAndSize[i][])
}
fileWriteclose()
preUploadsetStatus(GlobalASSEMBLED)
preUploadsetServerPath(uploadedUrl)
getBaseDao()update(uploadupdatePreUploadInfo preUpload)
if(GlobalBLOCK_UPLOAD_TO!=GlobalBLOCK_UPLOAD_TO_LOCAL)
{
//组装完毕没有问题 删除掉S/S上的block
String[] path=preUploadgetSBlockUrl()
for (String string : path) {
try {
if(GlobalBLOCK_UPLOAD_TO==GlobalBLOCK_UPLOAD_TO_S)
{
SUtildeleteFile(preUploadgetBucketUrl() string)
}else
{
SUtildeleteFile(preUploadgetBucketUrl() string)
}
} catch (Exception e) {
logerror(egetMessage() e)
}
}
}
if (logisInfoEnabled())
(preUploadgetUuid() + assembleFileWithBlock)
} catch (IOException e) {
logerror(egetMessage() e)
try {
if (fileReader != null) {
fileReaderclose()
}
if (fileWrite != null) {
fileWriteclose()
}
} catch (IOException ex) {
logerror(egetMessage() e)
}
}
}
BlockPreuploadFileInfo 是我们自定义的业务文件处理 bean
OK分块上传的服务器客户端源代码及其工作流程至此已全部介绍完毕以上源代码全部是经过项目实践过的大部分现在仍运行于一些项目之中有兴趣的朋友可以自己动手将以上代码自行改造看看能否运行成功如果遇到问题可以在本博客下跟帖留言大家一起讨论讨论