使用websocket进行断点续传文件
上传要点
断点上传的目标是为了将大型文件分批分片上传,在服务端进行接收并组合,最终完成上传
代码
websocket接收文件
/**
* websocket断点上传文件
* zhanghaichao
*/
@ServerEndpoint("/websocket/fileup")
public class FileServer {
/**
* 记录所有上传文件属性
*/
private static final Map<String,FileTargetInfo> fileTarge = new HashMap<>();
/**
* 当websocket连接成功的时候就是准备上传文件的时候
* @param session
*/
@OnOpen
public void onOpen(Session session) {
//扩大一次性上传的最大数值
session.setMaxBinaryMessageBufferSize(BreakpointUploadConfig.blobSize+8);
}
/**
* 当链接关闭的时候判断是否是上传成功状态,并进行删除未上传完文件或者保存文件操作
* @param session
*/
@OnClose
public void onClose(Session session) {
System.out.println("链接关闭");
String id = session.getId();
FileTargetInfo fileTargetInfo = fileTarge.get(id);
if(fileTargetInfo!=null){
fileTargetInfo.fileUploadComplete();
fileTarge.remove(id);
}
}
/**
* 接收要上传文件的属性,文件名称,大小,key值
* @param message
* @param session
*/
@OnMessage
public void onTextMessage(String message, Session session) {
try {
FileInfo fileInfo = Json.getJson().parse(message, FileInfo.class);
List<String> tList = TimeTools.getDayMonYear();
String savePath = String.format(BreakpointUploadConfig.savePath+"%s/%s/%s/",tList.get(0),tList.get(1),tList.get(2));
FileTargetInfo fileTargetInfo = new FileTargetInfo(fileInfo,savePath);
//替换一下文件存放位置,不然将使用BreakpointUploadConfig.savePath来存放
fileTarge.put(session.getId(),fileTargetInfo);
UploadCommand uc=new UploadCommand(fileTargetInfo);
response(session,uc);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 开始上传二进制文件
* @param session
* @param bb
*/
@OnMessage
public void onBinaryMessage(Session session,ByteBuffer bb) {
String id = session.getId();
FileTargetInfo fileTargetInfo = fileTarge.get(id);
fileTargetInfo.saveByteBuffer(bb);
UploadCommand uc=new UploadCommand(fileTargetInfo);
if(uc.getCompletePercent()==1){
FileTargetInfo fileTarget = fileTarge.get(id);
fileTarget.fileUploadComplete();
fileTarge.remove(id);
}
response(session,uc);
}
@OnError
public void onError(Throwable t) {
t.printStackTrace();
}
private void response(Session session,Object obj) {
try {
session.getBasicRemote().sendText(Json.getJson().toJson(obj));
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收文件属性
/**
* 待上传文件基本信息类
* @author zhc
*
*/
public class FileInfo {
private String fileName;
private String fileId;
private long fileSize;
private String fileInfo;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public String getFileInfo() {
return fileInfo;
}
public void setFileInfo(String fileInfo) {
this.fileInfo = fileInfo;
}
}
内部文件状态
/**
* 内部保存文件状态
*/
public class FileTargetInfo implements Serializable{
private static final long serialVersionUID = -892304047563801858L;
/**上传文件基本目录*///todo
private String savePath = BreakpointUploadConfig.savePath;
public int blobSize = BreakpointUploadConfig.blobSize;
/**上传文件信息*/
private FileInfo fileInfo;
/**文件上传进度*/
private float completePercent=0;
/**服务器端文件名*/
private String fileName;
/**
* 文件大小
*/
private long fileSize=0;
private long indexStart=0;
private long indexEnd=0;
private File file;
private File tempFile;
private RandomAccessFile raFile;
private FileChannel fileChannel;
public FileTargetInfo(FileInfo fileInfo,String savePath){
this.savePath = savePath;
this.initInfo(fileInfo);
}
public FileTargetInfo(FileInfo fileInfo){
this.initInfo(fileInfo);
}
private void initInfo(FileInfo fileInfo){
this.fileSize = fileInfo.getFileSize();
this.fileInfo = fileInfo;
this.fileName=fileInfo.getFileId()+"."+fileInfo.getFileName().substring(fileInfo.getFileName().lastIndexOf(".")+1);
if(this.blobSize>this.fileSize){
indexEnd = this.fileSize;
}else{
indexEnd = this.blobSize;
}
File filemkdirs =new File(this.savePath);
//如果文件夹不存在则创建
if(!filemkdirs .exists() && !filemkdirs .isDirectory())
{
filemkdirs .mkdirs();
}
//开始创建文件模板
this.file=new File(savePath+this.fileName);
this.tempFile=new File(savePath+this.fileInfo.getFileId()+".temp");
try {
this.raFile=new RandomAccessFile(this.tempFile, "rw");
this.raFile.setLength(this.fileInfo.getFileSize());
this.fileChannel=this.raFile.getChannel();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 保存数据
* @return
*/
public synchronized FileTargetInfo saveByteBuffer(ByteBuffer bb){
try {
this.fileChannel.write(bb, this.indexStart);
} catch (IOException e1) {
e1.printStackTrace();
}
if(this.indexEnd>=this.fileSize){//上传完毕
this.completePercent = 1;
this.indexEnd = -1;
this.indexStart = -1;
this.fileUploadComplete();
return this;
}
this.completePercent = (float)this.indexEnd/(float)this.fileSize;
//头变尾
this.indexStart = this.indexEnd;
if(this.fileSize-this.indexEnd>this.blobSize){
this.indexEnd+=this.blobSize;
}else{
this.indexEnd = this.fileSize;
}
return this;
}
public void fileUploadComplete(){
this.closeFileWriteAccessChannel();
if(this.completePercent>=1){
this.tempFile.renameTo(this.file);
}
this.tempFile.delete();
}
public void closeFileWriteAccessChannel(){
try {
this.fileChannel.close();
this.raFile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
public int getBlobSize() {
return blobSize;
}
public void setBlobSize(int blobSize) {
this.blobSize = blobSize;
}
public Long getFileSize() {
return fileSize;
}
public void setFileSize(Long fileSize) {
this.fileSize = fileSize;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public File getTempFile() {
return tempFile;
}
public void setTempFile(File tempFile) {
this.tempFile = tempFile;
}
public RandomAccessFile getRaFile() {
return raFile;
}
public void setRaFile(RandomAccessFile raFile) {
this.raFile = raFile;
}
public FileChannel getFileChannel() {
return fileChannel;
}
public void setFileChannel(FileChannel fileChannel) {
this.fileChannel = fileChannel;
}
public FileInfo getFileInfo() {
return fileInfo;
}
public void setFileInfo(FileInfo fileInfo) {
this.fileInfo = fileInfo;
}
public float getCompletePercent() {
return completePercent;
}
public void setCompletePercent(float completePercent) {
this.completePercent = completePercent;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getIndexStart() {
return indexStart;
}
public void setIndexStart(long indexStart) {
this.indexStart = indexStart;
}
public long getIndexEnd() {
return indexEnd;
}
public void setIndexEnd(long indexEnd) {
this.indexEnd = indexEnd;
}
}
用于返回json数据
/**
* 文件上传命令信息类
* 此类会被转成JSON发送到客户端
* @author zhanghaichao
*
*/
public class UploadCommand {
private final String typeId="uploadCommand";
private String fileId;
private int index;
private long indexStart;
private long indexEnd;
private long blobSize;
private float completePercent=0;
public UploadCommand(FileTargetInfo fileTargetInfo){
FileInfo fileInfo = fileTargetInfo.getFileInfo();
this.fileId = fileInfo.getFileId();
this.indexStart = fileTargetInfo.getIndexStart();
this.indexEnd = fileTargetInfo.getIndexEnd();
this.completePercent = fileTargetInfo.getCompletePercent();
this.blobSize = fileTargetInfo.getFileSize();
}
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public long getIndexStart() {
return indexStart;
}
public void setIndexStart(long indexStart) {
this.indexStart = indexStart;
}
public long getIndexEnd() {
return indexEnd;
}
public void setIndexEnd(long indexEnd) {
this.indexEnd = indexEnd;
}
public long getBlobSize() {
return blobSize;
}
public void setBlobSize(long blobSize) {
this.blobSize = blobSize;
}
public float getCompletePercent() {
return completePercent;
}
public void setCompletePercent(float completePercent) {
this.completePercent = completePercent;
}
public String getTypeId() {
return typeId;
}
}
html部分
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>FileUpload</title>
<script src="/static/js/jquery-2.1.1.min.js"></script>
</head>
<body>
<input type="file" id="file">
<input type="button" id="but" value="上传">
</body>
<script>
var file = $('#file');
var f;
file.on('change', function (e) {
var files = e.currentTarget.files;
f = files[0]
uploadFileInfo(f);
});
function uploadFileInfo(f) {
var fileInfo = {};
fileInfo.fileName = f.name;
fileInfo.fileSize = f.size;
fileInfo.fileInfo = f.type;
fileInfo.fileId = Date.parse(new Date());//写死一个hash值
ws.send(JSON.stringify(fileInfo));
}
function uploadFile(obj) {
var blob = f.slice(obj.indexStart, obj.indexEnd);
var reader = new FileReader();
reader.onload = function (e) {
ws.send(e.target.result);
};
reader.readAsArrayBuffer(blob);
}
var options = {
concurrentHash: 2,
concurrentUpload: 2,
debugMode: false,
wsuri: 'ws://localhost:8080/websocket/fileup'
};
var onmessage = function (that) {
return function (e) {
var obj = JSON.parse(e.data);
if (obj.typeId == 'uploadCommand') {
console.log('文件完成:' + parseInt(obj.completePercent * 65 + 35, 10) + '%');
if (obj.completePercent != 1) {
uploadFile(obj);
} else {
console.log('文件上传完成,开始上传下一个文件。');
}
}
}
}
var onopen = function (that) {
console.log('Websocket is opened.');
}
var ws;
$(function () {
ws = new WebSocket(options.wsuri);
ws.onopen = onopen(this);
ws.onmessage = onmessage(this);
});
</script>
</html>