ByteBuffer使用实例

文件上传Servlet实现解析
本文详细解析了如何使用Servlet实现文件上传功能,包括输入流读取、缓冲区操作及文件保存逻辑。

package com.vyloy.server;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.vyloy.server.db.File;
import com.vyloy.server.db.service.FileService;
import com.vyloy.server.fileupload.FileUpload;

/**
* Servlet implementation class UploadServlet
*/
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public UploadServlet() {
super();
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ByteBuffer result = ByteBuffer.allocate(3*1024*1024);
try {
ReadableByteChannel in = Channels.newChannel(request
.getInputStream());
ByteBuffer tempbytes = ByteBuffer.allocate(1024*1024);
while ((in.read(tempbytes)) != -1) {
tempbytes.flip();
//重点--比较临时字节数是否比输出字节缓存剩余容量大
if(result.remaining()<tempbytes.limit()){
ByteBuffer _result=ByteBuffer.allocate(result.capacity()*2);
result.flip();
_result.put(result);
result=_result;
}
result.put(tempbytes);
tempbytes.clear();
}
result.flip();
} catch (IOException e) {
e.printStackTrace();
response.getWriter().write("0");
return;
}
try{
//重点--result.array()会返回底层数组,如果有效数据少于容量,
//则数据中会包含很多没用的值为0的字节
//所以这里创建一个新的数组再把有效数据存入
byte[] bytes=new byte[result.limit()];
result.get(bytes);
result.clear();
FileUpload fileUpload = new FileUpload(bytes);
String fileName=new String(fileUpload.get("Filename"),Charset.forName("utf-8"));
byte[] data=fileUpload.get("Filedata");
File file = new File(fileName);
FileService.getInstance().save(file, data);
response.getWriter().write("1");
}catch(Exception e){
e.printStackTrace();
response.getWriter().write("0");
}
}

}



不变式
标记、位置、限制和容量值遵守以下不变式:

0 <= 标记 <= 位置 <= 限制 <= 容量
新创建的缓冲区总有一个 0 位置和一个未定义的标记。初始限制可以为 0,也可以为其他值,这取决于缓冲区类型及其构建方式。一般情况下,缓冲区的初始内容是未定义的。

清除、反转和重绕
除了访问位置、限制、容量值的方法以及做标记和重置的方法外,此类还定义了以下可对缓冲区进行的操作:

clear() 使缓冲区为一系列新的通道读取或相对放置 操作做好准备:它将限制设置为容量大小,将位置设置为 0。

flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。

rewind() 使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。


[color=white]作者:翁志艺[/color]
<think>好的,我现在需要帮助用户了解如何使用Java NIO的ByteBuffer类。用户明确要求教程、示例和详解,所以我得先理清ByteBuffer的基本概念和核心组件。首先,我记得ByteBufferJava NIO中的一个关键类,用于高效处理字节数据。根据引用[2],Java NIO提供了非阻塞、高效的I/O操作,而ByteBuffer是其核心之一。 首先,我应该从ByteBuffer的创建方法开始。用户可能需要知道如何分配一个缓冲区,所以需要提到allocate和allocateDirect的区别。引用[3]提到需要弄清楚position、limit、capacity这些变量,所以我得详细解释这些属性。 接下来是缓冲区的读写操作。用户可能需要示例代码来演示如何写入和读取数据。比如put()和get()方法的不同用法,包括绝对定位和相对定位。引用[1]提到put方法有很多版本,可以写入不同数据,这里需要具体说明如何操作。 然后是关于flip()、rewind()、clear()等方法的作用,这些是切换读写模式的关键步骤,容易混淆,必须详细解释。例如,flip()方法用于切换为读模式,调整limit和position的位置,而clear()则重置缓冲区但不清数据,这些都需要明确说明。 还需要涵盖标记(mark)和重置(reset)的用法,以及如何复制缓冲区,比如slice()和duplicate()方法。引用[4]提到的arrayOffset()方法也很重要,如果缓冲区有后备数组的话,用户可能需要知道如何访问底层数组的偏移量。 此外,应该提供一些完整的示例代码,展示从创建缓冲区到读写数据的完整流程,帮助用户理解各个步骤如何衔接。比如,写入字符串到ByteBuffer,然后读取出来,这样的例子比较直观。 最后,要确保回答结构清晰,分步骤讲解,并附上相关问题供用户进一步学习。需要检查所有LaTeX格式是否正确,行内公式用$...$,独立公式用$$...$$,但在这个回答中可能公式较少,主要关注代码块和引用标识。同时引用提供的四个参考文献,适当在相关部分添加引用标记,比如在提到NIO核心概念时引用[^2],在讲解position等变量时引用[^3]等。 总结一下,回答的结构应该是:概述、创建缓冲区、核心属性、读写操作、模式切换、其他方法、示例代码、常见问题,最后是相关问题。这样用户能够逐步学习ByteBuffer使用方法,并通过示例加深理解。</think>### Java NIO ByteBuffer 使用教程详解 #### 1. ByteBuffer 概述 Java NIO 的 $ByteBuffer$ 类是处理字节数据的核心工具,用于高效读写二进制数据(如文件、网络传输)。它通过直接内存访问和缓冲区管理优化 I/O 性能[^2]。 --- #### 2. 创建 ByteBuffer ```java // 分配堆内存缓冲区(JVM 堆内) ByteBuffer heapBuffer = ByteBuffer.allocate(1024); // 分配直接内存缓冲区(操作系统堆外,减少拷贝开销) ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024); // 包装现有数组(数据修改会同步到数组) byte[] arr = new byte[1024]; ByteBuffer wrappedBuffer = ByteBuffer.wrap(arr); ``` --- #### 3. 核心属性与关系 - **capacity**:缓冲区的最大容量(固定值) - **position**:当前读写位置(初始为 0) - **limit**:可读/写的最大位置(初始等于 capacity) - **mark**:临时标记位置(通过 `mark()` 设置) 转换关系如图: ```plaintext 0 ≤ mark ≤ position ≤ limit ≤ capacity ``` --- #### 4. 读写操作 **写入数据**(写模式): ```java byte[] data = "Hello".getBytes(); heapBuffer.put(data); // 相对写入(position 自动后移) heapBuffer.put(2, (byte)0x41); // 绝对写入(指定索引位置) ``` **读取数据**(读模式): ```java heapBuffer.flip(); // 切换为读模式(limit=原position,position=0) while (heapBuffer.hasRemaining()) { byte b = heapBuffer.get(); // 相对读取 } ``` --- #### 5. 模式切换方法 - **`flip()`**:写模式 → 读模式 $$limit = position,\quad position = 0$$ - **`clear()`**:重置缓冲区(不清数据) $$position = 0,\quad limit = capacity$$ - **`rewind()`**:重新读取数据 $$position = 0$$ --- #### 6. 其他重要方法 - **`slice()`**:创建共享数据的子缓冲区 - **`duplicate()`**:创建完全副本(共享底层数据) - **`arrayOffset()`**:获取缓冲区在底层数组的偏移量[^4] ```java if (heapBuffer.hasArray()) { int offset = heapBuffer.arrayOffset() + heapBuffer.position(); } ``` --- #### 7. 完整示例 ```java // 创建并写入数据 ByteBuffer buffer = ByteBuffer.allocate(128); buffer.put("NIO ByteBuffer".getBytes(StandardCharsets.UTF_8)); // 切换读模式 buffer.flip(); // 读取数据到数组 byte[] readData = new byte[buffer.remaining()]; buffer.get(readData); System.out.println(new String(readData)); // 输出 "NIO ByteBuffer" ``` --- #### 8. 常见问题 **Q:堆缓冲区与直接缓冲区的区别?** A:堆缓冲区由 JVM 管理,直接缓冲区通过操作系统本地内存分配(减少拷贝次数,适合高频 I/O 操作)。 **Q:如何避免缓冲区溢出?** A:操作前检查剩余空间:`if (buffer.remaining() >= neededSize) { ... }` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值