基础知识
java文件操作相关类
1.JDK原始的java.io.FileInputStream类
2.JDK原始的java.io.RandomAccessFile类
3.Apache Commons IO提供的org.apache.commons.io.FileUtils类
4.JDK1.7新增的基于NIO非阻塞异步读取文件的java.nio.channels.AsynchronousFileChannel类。
5.JDK1.7新增的基于NIO读取文件的java.nio.file.Files类。
常用方法
如:
Files.readAllBytes、Files.readAllLines
FileInputStream
FileOutputStream
File
FileUtils
IOUtils
BufferedReader
ServletFileUpload
MultipartFile
CommonsMultipartFile
PrintWriter
ZipInputStream
ZipEntry.getSize
典型运算思路
直接上传思路 multipartFile.transferTo(file);
DiskFileItemFactory
@MultipartConfig
MultipartFile
File
upload
InputStream
write
File操作相关函数
file.exists()判断存在
file.delete()删除文件
file.creatNewFile()
file.writer()
file.read()
file.readline()#读取行的思路
file.close()
一.文件上传
前言:现在这个漏洞的其实越来越少了,现在很多主流框架对上传的文件不会放在web可访问的页面下。
1.servlet框架文件上传方法
servlet3之前
使用commons-fileupload、commons-io这两个jar包来处理文件上传
servlet3之后
使用request.getParts()获取上传文件
2.最简单的任意文件上传代码写法
private boolean uploadWithAnnotation(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
Part part = request.getPart("fileName");
if(part == null) {
return false;
}
String filename = UPLOAD_PATH + File.separator + part.getSubmittedFileName();
part.write(filename);
part.delete();
return true;
}
3.修复方法
①判断context-type内容方法
https://mp.weixin.qq.com/s/LzVseWzLP3CjQsmYzvELZA
②文件上传改名
String fileExt = filename.substring(filename.lastIndexOf(".") + 1); //获取后缀名
String savename = UPLOAD_PATH + File.separator + DigestUtils.md5Hex(System.currentTimeMillis() + filename) + "." + fileExt;
③设置白名单后缀
if(!ESAPI.validator().isValidFileName("upload", filename, ALLOW_EXT, false)) {
response.getWriter().println("后缀名不合法");
}
④文件大小限制
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
servletFileUpload.setSizeMax(1024 * 400);
二.文件操作(增删读下)
1.文件写入
思路:利用fileWriter类的思路去写
// 创建文件
file.createNewFile();
// creates a FileWriter Object
FileWriter fileWriter = new FileWriter(filename);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write(fileContent);
bufferedWriter.close();
防御方法:
对写入的内容做一个过滤就好了
private boolean checkFilecontent(String fileContent) {
List<String> blackContents = Arrays.asList("<%@page", "<%!");
for(String blackContent : blackContents) {
if(fileContent.contains(blackContent)) {
return false;
}
}
return true;
}
2.文件删除
利用file.delete函数判断就好了
String filename = request.getParameter("filename");
File file = new File(filename);
if(file != null && file.exists() && file.delete()) {
response.getWriter().println("delete success");
} else {
response.getWriter().println("delete failed");
}
3.文件读取
两种思路
1.基于InputStream+outputStream的思路
思路:即先把内容写入进去,然后在把内容用outputStream读取出来
//InputStream
File file = new File(filename);
InputStream inputStream = new FileInputStream(file);
while(-1 != (len = inputStream.read())) {
outputStream.write(len);
}
2.基于File.reader的思路
//FileReader
String fileContent = "";
FileReader fileReader = new FileReader(filename);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = "";
while (null != (line = bufferedReader.readLine())) {
fileContent += (line + "\n");
}
4.文件下载
原理:当浏览器不能识别的格式时即进行下载到本地端
实现方法:
1.)设置Content-Type段
确定客户端或者服务器以那种端进行获取
application/octet-stream:文件拓展名.*(二进制流)
application/x-msdownload:文件拓展名.dll(告诉浏览器这是一个要保存到本地的下载文件)
2.Content-header设置浏览器是否以附件形式获取数据
HttpServletResponse.setHeader方法设置Content-Disposition的值为"attachment:filename=文件名",浏览器通过附件的形式来获取到用户上传的文件
3.InputStream
将文件内容输入到java内存中
outputStream
利用输出流将java内存中的数据输出到文件中去
InputStream inputStream = new FileInputStream(file);
ServletOutputStream outputStream = response.getOutputStream();
类似于文件下载的思路
常用变量
fileName
filePath
getFile
getWriter
//stream
String filename = request.getParameter("filename");
File file = new File(filename);
response.reset();//把response的内容给重置为默认的
response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes("utf-8")));
response.addHeader("Content-Length", "" + file.length());
response.setContentType("application/octet-stream; charset=utf-8");
InputStream inputStream = new FileInputStream(file);
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
int len;
while(-1 != (len = inputStream.read())) {
outputStream.write(len);
}
inputStream.close();
outputStream.close();
后面三个的防御思路相同
防御方法:对文件内容做一个检测
private boolean checkFilename(String filename) {
filename = filename.replace("\\", "/"); //消除平台差异
//将文件操作限定在指定目录中
File file = new File(filename);
if(file == null || !file.exists() || file.getCanonicalFile().getParent().equals(new File(DOWNLOAD_PATH).getCanonicalPath())) { //检测上级目录是否为指定目录
return false;
}
//检测文件名中是否有敏感字符
List<String> blackList = Arrays.asList("./", "../");
for(String badChar : blackList) {
if(filename.contains(badChar)) {
return false;
}
}
//对文件后缀名设置白名单
List<String> whiteExt = Arrays.asList("png", "jpg", "gif", "jpeg", "doc");
String fileExt = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
if(!whiteExt.contains(fileExt)) {
return false;
}
return true;
}