Java IO/NIO/AIO的知识体系图
博主最开始是用IO实现文件上传下载功能,但发现效率慢,于是使用了NIO
新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。
实体类
@Entity
public class DpOrder {
@Id
@GeneratedValue
private Integer id;
private String orderNo;
@Column(columnDefinition = "text")
private String filePath;
}
file实体类
public class FileBean implements Serializable {
private String filePath;
private String fileName;
public FileBean() {
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
控制层
@RestController
@RequestMapping("api/orderdatamanagement/dpordermanagement")
public class DpOrderController extends ExceptionResponse {
@Autowired
private DpOrderService dpOrderService;
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST, produces = "multipart/form-data;charset=utf8")
@ResponseBody
public void uploadFile(@RequestParam("orderNo") String orderNo, @RequestParam("files") MultipartFile[] files)
throws IOException {
dpOrderService.uploadFile(orderNo, files);
}
@RequestMapping(value = "/valid-dp-order-download", method = RequestMethod.GET, produces = "multipart/form-data;charset=utf8")
public void validDpOrderDownload(@RequestParam("orderNo") String orderNo) {
dpOrderService.validDpOrderDownload(orderNo);
}
@RequestMapping(value = "/download-files-list", method = RequestMethod.GET)
public ResponseEntity<byte[]> downFilesList(@RequestParam("orderNo") String orderNo, HttpServletResponse response) {
return dpOrderService.downFilesList(orderNo, response);
}
@RequestMapping(value = "/dp-order-file-paths", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public List<String> getDpOrderByFilePaths(@RequestParam String orderNo) {
return dpOrderService.getDpOrderByFilePaths(orderNo);
}
}
业务逻辑层
@Service
@Transactional
public class DpOrderServiceImpl implements DpOrderService {
private static final Logger logger = LoggerFactory.getLogger(DpOrderServiceImpl.class);
@Autowired
private DpOrderRepository dpOrderRepository;
@Override
public void uploadFile(String orderNo, MultipartFile[] files) throws IOException {
if (StringUtils.isEmpty(orderNo)) {
throw new DpOrderException("订单号为空.");
}
DpOrder dpOrder = dpOrderRepository.findByOrderNo(no[0]);
if (dpOrder == null) {
logger.error("订单号不存在.");
throw new DpOrderException("订单号不存在.");
}
String[] no = orderNo.split(",");
InputStream in;
OutputStream out;
String path = orderFileProperties.getFilePath();
File fileDir = new File(path);
if (!fileDir.exists()) {
fileDir.setWritable(true);
boolean mkdir = fileDir.mkdirs();
if (!mkdir) {
logger.error("创建文件夹失败.");
throw new DpOrderException("创建文件夹失败.");
}
}
for (MultipartFile file : files) {
String fileName = file.getOriginalFilename();
String originalName = fileName.substring(0, fileName.lastIndexOf("."));
String fileExtensionName = fileName.substring(fileName.lastIndexOf(".") + 1);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String combinationFileName = originalName + "_" + sdf.format(new Date()) + "." + fileExtensionName;
File serverFile = new File(fileDir.getAbsolutePath() + "/" + combinationFileName);
in = file.getInputStream();
out = new FileOutputStream(serverFile);
byte[] b = new byte[1024];
int len;
while ((len = in.read(b)) > 0) {
out.write(b, 0, len);
}
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
dpOrder.setFilePath(dpOrder.getFilePath() + combinationFileName + ",");
}
}
@Override
public List<String> getDpOrderByFilePaths(String orderNo) {
DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo);
String str = dpOrder.getFilePath();
if (StringUtils.isEmpty(str)) {
return null;
}
String[] ids = str.split(",");
ArrayList<String> filePathList = new ArrayList<>();
for (int i = 0; i < ids.length; i++) {
filePathList.add(ids[i]);
}
return filePathList;
}
@Override
public void validDpOrderDownload(String orderNo) {
DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo);
String str = dpOrder.getFilePath();
if (StringUtils.isEmpty(str)) {
throw new DpOrderException("没有文件可下载.");
}
}
@Override
public ResponseEntity<byte[]> downFilesList(String orderNo, HttpServletResponse response) {
DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo);
String str = dpOrder.getFilePath();
String[] ids = str.split(",");
ArrayList<FileBean> fileList = new ArrayList<>();
for (int i = 0; i < ids.length; i++) {
FileBean file = new FileBean();
file.setFileName(ids[i]);
file.setFilePath(orderFileProperties.getFilePath());
fileList.add(file);
}
String zipName = "download.zip";
response.setContentType("application/x-zip-compressed");
response.setHeader("Content-Disposition", "attachment; filename=" + zipName);
ZipOutputStream zipos = null;
try {
zipos = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()));
zipos.setMethod(ZipOutputStream.DEFLATED);
} catch (Exception e) {
e.printStackTrace();
}
DataOutputStream os = null;
for (int i = 0; i < fileList.size(); i++) {
String filePath = fileList.get(i).getFilePath();
String fileName = fileList.get(i).getFileName();
File file = new File(filePath + "/" + fileName);
try {
zipos.putNextEntry(new ZipEntry(fileName));
os = new DataOutputStream(zipos);
InputStream is = new FileInputStream(file);
byte[] b = new byte[100];
int length;
while ((length = is.read(b)) != -1) {
os.write(b, 0, length);
}
is.close();
zipos.closeEntry();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
os.flush();
os.close();
zipos.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
application.properties
#单个文件最大
spring.servlet.multipart.max-file-size=20MB
#设置总上传数据总大小
spring.servlet.multipart.max-request-size=400MB
效果如下
查看文件
选择上传文件
点击上传
再次查看文件
点击下载
DpOrder对象中,filePath属性存储文件
file_path里面的文件名以逗号相隔
文件上传下载功能已经完成了,但发现效率慢,尤其在服务器上,下载文件要几秒钟,大文件甚至更久
于是,就用效率更快的NIO
优化后的代码
@Override
public ResponseEntity<byte[]> downFilesList(String orderNo) {
long starttime = System.currentTimeMillis();
DpOrder dpOrder = dpOrderRepository.findByOrderNo(orderNo);
String str = dpOrder.getFilePath();
FileUtil.downZipFile(str, orderFileProperties);
long endtime = System.currentTimeMillis();
logger.info("下载文件花费的时间为:" + (endtime - starttime) + "毫秒");
return null;
}
public class FileUtil {
public static void downZipFile(String str, OrderFileProperties orderFileProperties) {
String[] ids = str.split(",");
String savePath = "D:\\download_files";
File downloadFile = new File(savePath);
if (!downloadFile.exists()) {
downloadFile.mkdirs();
downloadFile.setWritable(true);
}
String zipName = savePath + "\\" + FileUtil.random() + "_download.zip";
File zipFile = new File(zipName);
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
WritableByteChannel writableByteChannel = Channels.newChannel(zipOut)) {
zipOut.setMethod(ZipOutputStream.DEFLATED);
for (int i = 0; i < ids.length; i++) {
File file = new File(orderFileProperties.getFilePath() + "/" + ids[i]);
try (FileChannel fileChannel = new FileInputStream(file).getChannel()) {
zipOut.putNextEntry(new ZipEntry(ids[i]));
fileChannel.transferTo(0, file.length(), writableByteChannel);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
经测试,下载速度确实快多了,10M的大小的文件需要500左右的ms
教程会持续更新