%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.*,java.util.*,javax.servlet.*,javax.servlet.http.*" %>
<%@ page import="java.rmi.ServerException" %>
<%@ page import="java.nio.charset.Charset" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<%
//定义上传文件的最大字节数,这里设置的数值是 102400 * 102400 字节(不过这个数值看起来可能过大,也许是示例或特殊需求,通常会根据实际情况合理设置大小限制)
int MAX_SIZE = 102400 * 102400;
//创建根路径的保存变量,后续用于拼接出文件上传后的保存路径,初始时只是声明,还未赋值具体内容
String rootPath;
//声明文件读入类,用于从请求流中读取上传的数据,初始化为 null,后续会在合适的逻辑中进行实例化
DataInputStream in = null;
//声明文件输出流类,用于将上传的数据写入到服务器端的文件中,初始化为 null,后续会在合适的逻辑中进行实例化
FileOutputStream fileOut = null;
//取得互联网程序(即当前 Servlet 上下文对应的应用程序)的绝对地址,例如获取到类似项目部署在服务器上的根目录路径
String realPath = request.getSession().getServletContext().getRealPath("/");
//对获取到的绝对路径进行处理,截取掉其中 "\out" 及之后的部分,可能是为了获取到项目更合适的基础路径(具体取决于项目结构和需求)
realPath = realPath.substring(0, realPath.indexOf("\\out"));
// out.print(realPath);
// 创建文件的保存目录,将处理后的路径与 "\web\upload\" 拼接起来,形成文件最终要保存的目录路径,这里假设项目中有这样一个用于存放上传文件的目录结构
rootPath = realPath + "\\web\\upload\\";
//取得客户端上传的数据类型,例如判断是否是 "multipart/form-data" 这种常见的文件上传类型的数据格式
String contentType = request.getContentType();
try {
// 判断客户端上传的数据类型是否是 "multipart/form-data",如果是则进行文件上传相关的处理逻辑
if (contentType.indexOf("multipart/form-data") >= 0) {
// 读入上传数据,通过从请求输入流创建 DataInputStream 对象,以便按字节读取客户端上传的数据
in = new DataInputStream(request.getInputStream());
// 获取上传数据的总长度(字节数),用于后续循环读取数据以及判断是否超过设定的最大字节限制
int formDataLength = request.getContentLength();
// 检查上传的数据长度是否超过了预先定义的最大字节数,如果超过则输出提示信息并终止当前方法执行(直接返回)
if (formDataLength > MAX_SIZE) {
out.print("上传的字节不可以超过" + MAX_SIZE + "字节");
return;
}
// 保存上传文件的数据,创建一个字节数组,大小为上传数据的总长度,用于存放从请求中读取到的字节数据
byte dataBytes[] = new byte[formDataLength];
int byteRead = 0;
int totalBytesRead = 0;
// 上传的数据保存在 byte 数组里面,通过循环从输入流中读取数据,每次读取一部分字节,直到读取完所有上传的数据
while (totalBytesRead < formDataLength) {
byteRead = in.read(dataBytes, totalBytesRead, formDataLength);
totalBytesRead += byteRead;
}
// 根据 byte 数组创建字符串,使用指定的 UTF-8 编码将字节数组转换为字符串,方便后续对上传数据内容进行字符串相关的操作(比如提取文件名等信息)
String file = new String(dataBytes, StandardCharsets.UTF_8);
// 取得上传数据的文件名,通过对转换后的字符串进行一系列的截取操作,从包含文件名信息的字符串中提取出真正的文件名(处理了包含文件名的特定格式字符串)
String saveFile = file.substring(file.indexOf("filename=\"") + 10);
saveFile = saveFile.substring(0, saveFile.indexOf("\n"));
saveFile = saveFile.substring(saveFile.lastIndexOf("\\") + 1, saveFile.indexOf("\""));
int lastIndex = contentType.lastIndexOf("=");
// 取得数据的分隔字符串,从上传数据的类型字符串(例如 "multipart/form-data; boundary=----WebKitFormBoundaryxxx")中提取出用于分隔不同部分的边界字符串(boundary)
String boundary = contentType.substring(lastIndex + 1, contentType.length());
// 创建保存路径的文件名,将前面确定的文件保存目录路径与提取出的文件名拼接起来,形成完整的文件保存路径(包含文件名和目录)
String fileName = rootPath + saveFile;
int pos;
pos = file.indexOf("filename = \"");
pos = file.indexOf("\n", pos) + 1;
pos = file.indexOf("\n", pos) + 1;
pos = file.indexOf("\n", pos) + 1;
int boundaryLocation = file.indexOf(boundary, pos) - 4;
// 取得文件数据的开始的位置,通过对文件内容字符串进行一系列操作,先获取相关子字符串,再计算其字节长度来确定文件数据在整个上传数据中的起始位置
int startPos = ((file.substring(0, pos)).getBytes()).length;
int endPos = ((file.substring(0, boundaryLocation)).getBytes()).length;
File checkFile = new File(fileName);
// 检查要保存的文件是否已经存在,如果存在则输出提示信息并终止当前方法执行(直接返回)
if (checkFile.exists()) {
out.println("" + saveFile + "文件已经存在.
");
return;
}
// 检查上传文件的目录是否存在,如果不存在则创建该目录(包括上级目录等,会递归创建),确保文件保存的目录是存在的
File fileDir = new File(rootPath);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
// 创建文件的输出类,通过指定文件保存路径实例化 FileOutputStream,用于将上传的数据写入到对应的文件中
fileOut = new FileOutputStream(fileName);
// 保存文件的数据,将之前读取到的字节数组中对应文件数据部分写入到文件输出流中,实现文件在服务器端的保存
fileOut.write(dataBytes, startPos, (endPos - startPos));
fileOut.close();
out.print("文件上传成功");
} else {
// 如果上传的数据类型不是 "multipart/form-data",则输出提示信息,告知用户需要上传该类型的文件
String content = request.getContentType();
out.print("上传的文件类型是" + content + "类型的,请上传目录mutipart/form-data类型的文件");
}
} catch (Exception ex) {
// 如果在文件上传过程中出现任何异常,捕获异常并抛出一个 ServerException,将原始异常的消息传递进去,方便在更上层进行统一的异常处理和日志记录等操作
throw new ServerException(ex.getMessage());
}
%>