1、文件上传时请求的消息类型
@Data
public class FilePacketRequestMessage extends Message{
//文件名称
private String file_md5;
//字节读取开始位置
private long startPos;
private byte[] bytes;
//字节读取结束位置
private int endPos;
//本次发送总字节数
private long totalLen;
//上传文件的路径,防止文件过大多次上传
private String filePath;
public FilePacketRequestMessage() {
}
@Override
public int getMessageType() {
return UploadOABak;
}
}
2、将读取到的最新备份封装到消息类型中并发送给服务端
@Slf4j
public class SendFile {
private static RandomAccessFile randomAccessFile = null;
public static void sendFileByByte(Channel channel, File file,long start) throws IOException {
String filename = file.getName();
String bakPath = file.getPath();
randomAccessFile = new RandomAccessFile(bakPath,"r");
byte[] bytes = new byte[1024];
int byteRead = 0;
FilePacketRequestMessage fileRequest = new FilePacketRequestMessage();
fileRequest.setFile_md5(filename);
fileRequest.setStartPos(start);
randomAccessFile.seek(fileRequest.getStartPos());
long totalLen = bakPath.length();
long end = totalLen - start;
log.info("传输文件第 {} 字节,共有{} 字节,还剩余{}未上传", start,totalLen,end);
fileRequest.setTotalLen(totalLen);
if ((byteRead = randomAccessFile.read(bytes))!= -1 && (randomAccessFile.length()-start)>0){
fileRequest.setEndPos(byteRead);
fileRequest.setBytes(bytes);
fileRequest.setFilePath(bakPath);
channel.writeAndFlush(fileRequest);
randomAccessFile.close();
}else{
randomAccessFile.close();
channel.close();
System.out.println("文件读取完成");
}
}
3、如果备份过大可能要拆分成多块进行上传
创建文件上传响应类型,监控已经上传完成度进度
并通过已经完成的进度作为开始再读取1024个字节作为下次要发送的数据
@Data
public class FileUploadResultResponseMessage extends AbstractResponseMessage{
private Long start;
private String filePath;
public FileUploadResultResponseMessage(Long start, String filePath) {
this.start = start;
this.filePath = filePath;
}
public FileUploadResultResponseMessage(boolean success, String reason, Long start, String filePath) {
super(success, reason);
this.start = start;
this.filePath = filePath;
}
@Override
public int getMessageType() {
return UploadResult;
}
}
4、读取服务端响应的消息,以上次结束的位置作为本次读取数据的开始进行上传。
@Slf4j
@Component
@ChannelHandler.Sharable
public class TransferFileHandler extends SimpleChannelInboundHandler<FileUploadResultResponseMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FileUploadResultResponseMessage in) throws Exception {
if (!in.getSuccess()) {
String filePath = in.getFilePath();
Long start = in.getStart();
File file = new File(filePath);
SendFile.sendFileByByte(ctx.channel(), file, start);
}
}
}
5、配置自动任务,定时定点调用文件上传的方法。
为防止网络异常应使用心跳机制实时感应客户端或服务端是否在线。
@Component
public class XXLJobTask {
@Value("${netty.uploadDir}")
private String uploadDir;
@Scheduled(initialDelay = 12000,fixedDelay = 1000*3600)
public void uploadFileTask(){
Channel channel = NettyClient.getChannel();
File bakPath = selectFileToUpload(Paths.get(uploadDir));
try {
SendFile.sendFileByByte(channel,bakPath,0);
} catch (IOException e) {
e.printStackTrace();
}
}
}