对于打文件上传如果网突然断了,得重新来一遍,是不是很郁闷,怎么办,如果浏览器不支持FileAPI 那就只能使用 Flash来做断点上传,有了HTML5 基于Ajax或者WebSocket 的原生实现也很棒。
下面就是基于websocket + spring 的一个实现例子
好了直接上代码
ws = new WebSocket( "ws://localhost:8080/bizplant/document/receive.ws" );
$( document ).ready( function() {
$( "#disconnect" ).click( function() {
if ( ws != null ) {
ws.close();
ws = null;
}
} );
$( "#connect" ).click( function() {
if ( ws == null ) {
ws = new WebSocket( "ws://localhost:8080/bizplant/document/receive.ws" );
}
} );
$( "#continue" ).click( function() {
var id = $("#fileId").val();
if ( !ws ) {
ws = new WebSocket( "ws://localhost:8080/bizplant/document/receive.ws" );
}
$.post( "http://localhost:8080/bizplant/document/queryFileUploadState.json", { "id": id }, function( data ) {
if ( data ) {
alert( "请选择续传文件" );
$( "#uploadImage" ).change( function() {
$( '
ws.send( JSON.stringify( data.fileUploadState.file ) );
var files = $( this ).get( 0 ).files;
var size = files[0].size;
var total = data.fileUploadState.loadedSize;
var unit = 8192;
var buffer = null;
log( "Info:" + data.fileUploadState.file.name + " 开始发送文件" );
while ( total < size ) {
if ( total + unit < size ) {
buffer = files[0].slice( total, total + unit );
total = total + unit;
} else {
buffer = files[0].slice( total, size );
total = size;
}
ws.send( buffer );
}
} );
}
}, "json" );
} );
$( 'input[type="file"]' ).not("#uploadImage").change( function() {
var files = $( this ).get( 0 ).files;
var fileCount = files.length;
$( this ).hide();
$( this ).siblings( "input" ).hide();
for ( var i = 0; i < fileCount; i++ ) {
var file = { };
file.size = files[i].size;
file.name = files[i].name;
file.fileType = { };
file.fileType.extension = files[i].name.substring( files[i].name.lastIndexOf( "." ) + 1 );
file.fileType.mimeType = files[i].type;
$( '
ws.send( JSON.stringify( file ) );
var size = files[i].size;
var total = 0;
var unit = 8192;
var buffer = null;
log( "Info:" + i + files[i].name + " 开始发送文件" );
while ( total < size ) {
if ( total + unit < size ) {
buffer = files[i].slice( total, total + unit );
total = total + unit;
} else {
buffer = files[i].slice( total, size );
total = size;
}
ws.send( buffer );
}
}
} );
ws.onopen = function() {
log( 'Info: connection opened.' );
};
ws.onmessage = function( event ) {
var loader = JSON.parse( event.data );
if ( parseInt( loader.load ) !== parseInt( loader.total ) ) {
$( 'div[file="' + loader.name + '"] .progress-bar' ).attr( "aria-valuenow", loader.percent ).css( "width", loader.percent + "%" );
$( 'div[file="' + loader.name + '"] .fileLoadSize' ).text( loader.percent );
} else {
$( 'div[file="' + loader.name + '"] .progress-bar' ).attr( "aria-valuenow", "100" ).css( "width", "100%" );
$( 'div[file="' + loader.name + '"] .fileLoadSize' ).text( "100" );
$( 'div[file="' + loader.name + '"]' ).removeClass( "active" );
}
log( 'Received: ' + event.data );
};
ws.onclose = function( event ) {
alert(event.reason);
var status = { "type": event.type, "code": event.code, "reason": event.reason, "time": new Date( event.timeStamp ) };
log( 'Info: connection closed.' );
log( JSON.stringify( status ) );
};
function log( message ) {
// var console = document.getElementById( 'console' );
// var p = document.createElement( 'p' );
// p.style.wordWrap = 'break-word';
// p.appendChild( document.createTextNode( message ) );
// console.appendChild( p );
// while ( console.childNodes.length > 10 ) {
// console.removeChild( console.firstChild );
// }
// console.scrollTop = console.scrollHeight;
}
} );
用户注册
spring 配置
FileUploadSocketHandler
import com.chameleon.document.DocumentBusiness;
import com.chameleon.document.model.File;
import com.chameleon.document.model.FileDiskContent;
import com.chameleon.document.model.FileUploadState;
import com.chameleon.util.Sequence;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
/**
*
* @author Johnson.zhang
*/
public class FileUploadSocketHandler extends AbstractWebSocketHandler {
private static final Logger logger = LoggerFactory.getLogger(FileUploadSocketHandler.class);
private Map result = null;
@Resource(name = "documentBusinessImpl")
private DocumentBusiness documentBusiness;
private ObjectMapper objectMapper = new ObjectMapper();
private OutputStream outputStream;
private long fileTotalSize;
private long fileLoadSize;
private DecimalFormat percentFormat = new DecimalFormat("0");
private FileDiskContent fileDiskContent = null;
@Resource(name = "systemFileUploadPath")
private String uploadFilePath;
@Resource(name = "sequenceSerialNumberImpl")
private Sequence sequence;
public void setUploadFilePath(String uploadFilePath) {
this.uploadFilePath = uploadFilePath;
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public void setSequence(Sequence sequence) {
this.sequence = sequence;
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String id = session.getId();
logger.debug("WebSocket连接已经建立,连接ID为:" + id);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
result = new HashMap();
result.put("buffer", 8192);
fileTotalSize = 0l;
fileLoadSize = 0l;
String msg = message.getPayload();
fileDiskContent = objectMapper.readValue(msg, FileDiskContent.class);
if (fileDiskContent.getId() != null && !fileDiskContent.getId().isEmpty()) {
//续传文件
CloseStatus status = null;
String filePath = fileDiskContent.getPath();
if (filePath == null || filePath.isEmpty()) {
status = new CloseStatus(2001, "续传文件路径不能为空!");
}
java.io.File tempFile = new java.io.File(uploadFilePath + "/" + filePath);
if (!tempFile.exists()) {
status = new CloseStatus(2002, "续传文件不存在!");
}
if (!tempFile.isFile()) {
status = new CloseStatus(2002, "续传文件不能是文件夹!");
}
if (status != null) {
session.close(status);
String rs = objectMapper.writeValueAsString(status);
logger.debug("WebSocket 文件断点续传初始化发生错误: " + rs);
return;
}
outputStream = new FileOutputStream(uploadFilePath + "/" + filePath, true);
fileTotalSize = fileDiskContent.getSize();
fileLoadSize = tempFile.length();
if (fileTotalSize > 0) {
double percent = ((double) fileLoadSize) / fileTotalSize * 100;
result.put("percent", percentFormat.format(percent));
}
result.put("id", fileDiskContent.getId());
result.put("name", fileDiskContent.getName());
result.put("total", fileDiskContent.getSize());
result.put("path", filePath);
result.put("load", fileLoadSize);
logger.debug("WebSocket 文件上传信息为: " + msg);
} else {
//新上传文件
String filePath = sequence.getId();
String tempPath = uploadFilePath + "/" + filePath;
outputStream = new FileOutputStream(tempPath);
fileDiskContent.setPath(filePath);
fileDiskContent.setId(sequence.getId());
fileTotalSize = fileDiskContent.getSize();
result.put("id", fileDiskContent.getId());
result.put("name", fileDiskContent.getName());
result.put("total", fileDiskContent.getSize());
result.put("path", filePath);
result.put("load", fileLoadSize);
result.put("percent", "0");
logger.debug("WebSocket 文件上传信息为: " + msg);
logger.debug("WebSocket 文件保存路径为: " + tempPath);
}
String rs = objectMapper.writeValueAsString(result);
session.sendMessage(new TextMessage(rs));
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
ByteBuffer msg = message.getPayload();
byte[] buffer = msg.array();
outputStream.write(buffer);
fileLoadSize = fileLoadSize + buffer.length;
logger.debug("WebSocket 文件已经上传了 [ " + fileLoadSize + " ] 字节");
result.put("load", fileLoadSize);
if (fileTotalSize > 0) {
double percent = ((double) fileLoadSize) / fileTotalSize * 100;
result.put("percent", percentFormat.format(percent));
}
if (fileLoadSize == fileTotalSize) {
closeStream();
if (fileDiskContent != null) {
if (fileDiskContent.getMeta() == null) {
documentBusiness.saveFile(fileDiskContent);
} else {
documentBusiness.deleteFileUploadStateByFile(fileDiskContent);
}
}
logger.debug("WebSocket 文件已经保存到磁盘:【" + uploadFilePath + "/" + fileDiskContent.getPath() + "】");
logger.debug("WebSocket 文件信息已经保存到数据库 文件ID为:【" + fileDiskContent.getId() + "】");
}
String rs = objectMapper.writeValueAsString(result);
logger.debug("WebSocket 文件上传回显数据位 :【" + rs + "】");
session.sendMessage(new TextMessage(rs));
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
String rs = objectMapper.writeValueAsString(exception);
if (session.isOpen()) {
session.sendMessage(new TextMessage(rs));
}
logger.debug("WebSocket 程序发生异常 :【" + rs + "】");
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
closeStream();
saveFileUploadState();
String rs = objectMapper.writeValueAsString(status);
logger.debug("WebSocket 连接关闭 :【" + rs + "】");
}
private void closeStream() throws Exception {
if (outputStream != null) {
outputStream.close();
outputStream = null;
}
}
private void saveFileUploadState() {
if (fileLoadSize < fileTotalSize) {
if (fileDiskContent.getMeta() == null) {
FileUploadState fileUploadState = new FileUploadState();
fileUploadState.setId(sequence.getId());
fileUploadState.setLoadedSize(fileLoadSize);
fileUploadState.setFile(fileDiskContent);
documentBusiness.saveFileUploadState(fileUploadState);
} else {
documentBusiness.updateFileUploadStateByFile(fileDiskContent, fileLoadSize);
}
}
}
}