有的时候我们可能是通过网络接收到的报文,报文的内容其实是文件片段,一个文件的报文可能非常多,而且可能不是有序的。
比如这样一个场景:
文件按照字节切分为片段,通过网络传送,报文自身保存了总包数以及子包号,如何快速有效的合成文件呢?
可以借助于Java中的nio实现
fileChannel.write(byteBuffer, position)
/**
* Writes a sequence of bytes to this channel from the given buffer,
* starting at the given file position.
*
* <p> This method works in the same manner as the {@link
* #write(ByteBuffer)} method, except that bytes are written starting at
* the given file position rather than at the channel's current position.
* This method does not modify this channel's position. If the given
* position is greater than the file's current size then the file will be
* grown to accommodate the new bytes; the values of any bytes between the
* previous end-of-file and the newly-written bytes are unspecified. </p>
*
* @param src
* The buffer from which bytes are to be transferred
*
* @param position
* The file position at which the transfer is to begin;
* must be non-negative
*
* @return The number of bytes written, possibly zero
*
* @throws IllegalArgumentException
* If the position is negative
*
* @throws NonWritableChannelException
* If this channel was not opened for writing
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws AsynchronousCloseException
* If another thread closes this channel
* while the write operation is in progress
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the write operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
public abstract int write(ByteBuffer src, long position) throws IOException;
这个方法可以通过指定保存的位置,进行文件字节的写入。
通常来说数据的发送代码的设计,除非是最后一包,否则前面应该都是相同大小的
这样通过子包号就可以计算每一个包应该的位置,类似于分页
核心代码
文件通道
File file = new File("d:/test.exe");
FileOutputStream fileOutputStream = new FileOutputStream(file);
FileChannel fileChannel = fileOutputStream.getChannel();
写文件
通常是循环接收文件的过程中进行处理
sendContents 表示读取到的字节 position 表示位置,packNo 表示子包号
定长的时候,只有最后一包可能大小不同,所以除了最后一包,处理时可以使用下面的逻辑
最后一包处理完前面的最后处理即可,细节可以根据业务情况自己确定
byte[] sendContents = something.getBytes;
long position = (packNo - 1) * sendContents.length;
// 将字符串放入缓存区
ByteBuffer byteBuffer = ByteBuffer.wrap(sendContents);
fileChannel.write(byteBuffer, position);
补充:
写文件时候的position 序号是从0 开始的,不要搞错了
子包号也需要关注下是0开始还是1 开始,上面的样例代码是从1 开始的
不需要关注文件大小的问题,会根据position 有一些调整,只需要按照position写即可
转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-101-1-1.html