说明
因为对于JAVA NIO我用的不是特别熟练,所以此博客仅仅是我的学习笔记,写的不好,好望海涵。
备注: Global是一些公共的常量存储的类。
public static final String BL_MODEL_URL="files_upload/bl_model/";
public static final String NOT_FOUND = "static/images/nopic.jpg";
public static final String NOT_FOUND = "static/images/nopic.jpg";
private static String BL_WEB_UPLOAD_PATH = "";
private static String BL_UPLOAD_PATH = "";
private static String BL_PREFIX_PATH = "uploads/";
private static String BL_MODEL_PATH = "";
public static String getWebUploadPath() {
return BL_WEB_UPLOAD_PATH;
}
public static void setWebUploadPath(String path) {
if(!path.endsWith("/"))
BL_WEB_UPLOAD_PATH = path + "/";
else
BL_WEB_UPLOAD_PATH = path;
}
public static String getWebUploadPath() {
return BL_WEB_UPLOAD_PATH;
}
private static void getUploadPath() {
String dir = "";
if (StringUtils.isBlank(BL_UPLOAD_PATH)) {
String uploadpath = getConfig("UPLOAD_PATH");
if (StringUtils.isBlank(uploadpath)) {
dir = BL_WEB_UPLOAD_PATH;
}
else
dir = uploadpath;
if(!dir.endsWith("/")) {
dir += "/";
}
BL_UPLOAD_PATH = dir + BL_PREFIX_PATH;
File file = new File(BL_UPLOAD_PATH);
if (!file.exists())
file.mkdirs();
}
}
public static String getUploadModelPath() {
if (StringUtils.isBlank(BL_UPLOAD_PATH))
getUploadPath();
if (StringUtils.isBlank(BL_MODEL_PATH)) {
BL_MODEL_PATH = BL_UPLOAD_PATH + "model/";
File file = new File(BL_MODEL_PATH);
if (!file.exists())
file.mkdirs();
}
return BL_MODEL_PATH;
}
以上就是NIO下载文件方法中定义的常量信息,至于Applog就是用来记录该方法启动时间的,如果想运行完全可以去掉,不必保留。
@RequestMapping(value="/pictureNIO")
public void pictureNio(HttpServletRequest request,
HttpServletResponse response, String mpid,String modelid
) throws Exception {
// 设置文件输出类型
response.setContentType("image/jpeg");
ServletOutputStream op = response.getOutputStream();
// 获取项目根目录
String path = request.getServletContext().getRealPath("/");
String downLoadPath = path + Global.BL_MODEL_PIC_URL + mpid;
File file = new File(downLoadPath);
if(!file.exists()) {
downLoadPath = path+Global.NOT_FOUND;
}
FileInputStream fileInputStream = new FileInputStream(file);
FileChannel fileChannel = fileInputStream.getChannel();
ByteBuffer bb = ByteBuffer.allocateDirect(2048);
byte[] barray = null;
int nRead=0;
try {
while ((nRead = fileChannel.read(bb)) >0) {
bb.flip();
barray = new byte[nRead];
bb.get(barray);
op.write(barray);
bb.clear();
/* if (nRead == 0)
continue;
bb.position(0);
bb.limit(nRead);
while (bb.hasRemaining()) {
nGet = Math.min(bb.remaining(), bufferSize);
// read bytes from disk
bb.get(barray, 0, nGet);
// write bytes to output
op.write(barray);
}
bb.clear();*/
}
} catch (IOException e) {
e.printStackTrace();
} finally {
bb.clear();
fileChannel.close();
fileInputStream.close();
}
}
首先我通过ServletOutputStream 获取到了响应的输出流,因为我之后会将读取到的信息放在其中(代码可能有点糙,并未做优化);通过前端传来的参数通过File判断该文件是否存在,不存在就返回404图片(NOT_FOUND是我在全局配置类中定义的静态常量,其中赋的是地址);如果存在,就通过FileInputStream 获取该图片,并打开输入通道;通道中的数据不为空,就将字节缓存中的数据放在字节数组中,写入数据流中。
通过JMeter接口压力测试,我发现在传输相同文件情况下,NIO平均的响应时间要比IO的慢,(我设置的线程组为1000,并发时间为20秒),也许是我写的NIO代码哪里写错了,影响了性能,毕竟我是第一次用NIO传输文件,所以请见谅。如果有更好的实现方法请一定,告诉我,我实现出来,并一定会接口压力测试一下,然后更新在博客中。
@RequestMapping(value="/pictureNIO")
public void pictureNio(HttpServletRequest request,
HttpServletResponse response, String mpid,String modelid
) throws Exception {
long start = System.currentTimeMillis();
String requestIp = request.getRemoteAddr();
int intCode = 0;
// 设置文件输出类型
response.setContentType("image/jpeg");
ServletOutputStream op = response.getOutputStream();
String downLoadPath = Global.getUploadModelPath() + mpid;
File file = new File(downLoadPath);
if(!file.exists()) {
intCode = Global.NO_DATA;
downLoadPath = Global.getWebUploadPath() + Global.NOT_FOUND;
}
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel fileChannel = raf.getChannel();
long size = file.length();
//将文件所有数据映射到虚拟内存,并只读
MappedByteBuffer buff = fileChannel.map(FileChannel.MapMode.READ_ONLY ,0, fileChannel.size());
byte[] by = new byte[(int) size];
try {
for(int i =0;i<size;i++) {
byte b = buff.get();
by[i] = b;
}
op.write(by);
} catch (Exception e) {
intCode = Global.IO_ERROR;
e.printStackTrace();
} finally {
buff.clear();
fileChannel.close();
raf.close();
op.close();
}
long end = System.currentTimeMillis();
long time = end - start;
AppLog.insert("modelpic/picture", intCode, time, requestIp, dataSource);
}
这是我经过改良过后的接口,速度比之前快了不少,并且我也做了测试,记录毫秒数。
同样是30.37 KB的文件,IO的速度要比NIO慢了超过10毫秒,不过对于同样的14.10KB的文件,两者相差不多。
这是我通过JMeter接口压力测试得出的结果,在1000个线程进行访问时,NIO平均每个请求的响应时间要比IO快,效率要高于IO,(文件大小为30.37 KB),如果有更好的改良请一定下方评论告诉我,我尽力实现出来,然后,并进行接口压力测试,来证明结论。
有人提议说使用Google的压缩插件将文件压缩一半试一试。
结果的确是快了,平均相应时间快了3毫秒。