整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

都什么年代了,你还不知道 Servlet3.0 中的文件上传方式?

天我们从源码角度来聊聊这个话题。

理解源码的前提是先会用,所以我们还是先来看看用法,然后再来分析源码。



1.两种文件解析方案

对于上传文件的请求,SpringMVC 中目前共有两种不同的解析方案:

  • StandardServletMultipartResolver
  • CommonsMultipartResolver

StandardServletMultipartResolver 支持 Servlet3.0 中标准的文件上传方案,使用非常简单;CommonsMultipartResolver 则需要结合 Apache Commons fileupload 组件一起使用,这种方式兼容低版本的 Servlet。

StandardServletMultipartResolver

先来回顾下 StandardServletMultipartResolver 的用法。

使用 StandardServletMultipartResolver,可以直接通过 HttpServletRequest 自带的 getPart 方法获取上传文件并保存,这是一种标准的操作方式,这种方式也不用添加任何额外的依赖,只需要确保 Servlet 的版本在 3.0 之上即可。

首先我们需要为 Servlet 配置 multipart-config,哪个 Servlet 负责处理上传文件,就为哪个 Servlet 配置 multipart-config。在 SpringMVC 中,我们的请求都是通过 DispatcherServlet 进行分发的,所以我们就为 DispatcherServlet 配置 multipart-config。

配置方式如下:

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</serv
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-servlet.xml</param-value>
    </init-param>
    <multipart-config>
        <location>/tmp</location>
        <max-file-size>1024</max-file-size>
        <max-request-size>10240</max-request-size>
    </multipart-config>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

然后在 SpringMVC 的配置文件中提供一个 StandardServletMultipartResolver 实例,注意该实例的 id 必须为 multipartResolver

<bean class="org.springframework.web.multipart.support.StandardServletMultipartResolver" id="multipartResolver">
</bean>

配置完成后,我们就可以开发一个文件上传接口了,如下:

@RestController
public class FileUploadController {
    SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/");

    @PostMapping("/upload")
    public String fileUpload(MultipartFile file, HttpServletRequest req) {
        String format = sdf.format(new Date());
        String realPath = req.getServletContext().getRealPath("/img") + format;
        File folder = new File(realPath);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        String oldName = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
        try {
            file.transferTo(new File(folder, newName));
            return req.getScheme() + "://" + req.getRemoteHost() + ":" + req.getServerPort() + "/img" + format + newName;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "error";
    }

    @PostMapping("/upload2")
    public String fileUpload2(HttpServletRequest req) throws IOException, ServletException {
        StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
        MultipartFile file = resolver.resolveMultipart(req).getFile("file");
        String format = sdf.format(new Date());
        String realPath = req.getServletContext().getRealPath("/img") + format;
        File folder = new File(realPath);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        String oldName = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
        try {
            file.transferTo(new File(folder, newName));
            return req.getScheme() + "://" + req.getRemoteHost() + ":" + req.getServerPort() + "/img" + format + newName;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "error";
    }

    @PostMapping("/upload3")
    public String fileUpload3(HttpServletRequest req) throws IOException, ServletException {
        String other_param = req.getParameter("other_param");
        System.out.println("other_param = " + other_param);
        String format = sdf.format(new Date());
        String realPath = req.getServletContext().getRealPath("/img") + format;
        File folder = new File(realPath);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        Part filePart = req.getPart("file");
        String oldName = filePart.getSubmittedFileName();
        String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
        try {
            filePart.write(realPath + newName);
            return req.getScheme() + "://" + req.getRemoteHost() + ":" + req.getServerPort() + "/img" + format + newName;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "error";
    }
}

我这里一共提供了三个文件上传接口,其实最终都是通过 StandardServletMultipartResolver 进行处理的。

  1. 第一个接口是我们在 SpringMVC 框架中常见的一种文件上传处理方式,直接在参数中写上 MultipartFile,这个 MultipartFile 其实就是从当前请求中解析出来的,具体负责参数解析工作的就是 RequestParamMethodArgumentResolver。
  2. 第二个接口其实是一种古老的文件上传实现方案,参数就是普通的 HttpServletRequest,然后在参数里边,我们再手动利用 StandardServletMultipartResolver 实例进行解析(这种情况可以不用自己 new 一个 StandardServletMultipartResolver 实例,直接将 Spring 容器中的注入进来即可)。
  3. 第三个接口我们就利用了 Servlet3.0 的 API,调用 getPart 获取文件,然后再调用对象的 write 方法将文件写出去即可。

大致上一看,感觉办法还挺多,其实仔细看,万变不离其宗,一会我们看完源码,相信小伙伴们还能变化出更多写法。

CommonsMultipartResolver

CommonsMultipartResolver 估计很多人都比较熟悉,这个兼容性很好,就是有点过时了。使用 CommonsMultipartResolver 需要我们首先引入 commons-fileupload 依赖:

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

然后在 SpringMVC 的配置文件中提供 CommonsMultipartResolver 实例,如下:

<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">-->
</bean>

接下来开发文件上传接口就行了:

@PostMapping("/upload")
public String fileUpload(MultipartFile file, HttpServletRequest req) {
    String format = sdf.format(new Date());
    String realPath = req.getServletContext().getRealPath("/img") + format;
    File folder = new File(realPath);
    if (!folder.exists()) {
        folder.mkdirs();
    }
    String oldName = file.getOriginalFilename();
    String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
    try {
        file.transferTo(new File(folder, newName));
        return req.getScheme() + "://" + req.getRemoteHost() + ":" + req.getServerPort() + "/img" + format + newName;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return "error";
}

这个就没啥好说,比较容易。

用法掌握了,接下来我们来看原理。

2.StandardServletMultipartResolver

不废话,直接来看看源码:

public class StandardServletMultipartResolver implements MultipartResolver {
	private boolean resolveLazily = false;
	public void setResolveLazily(boolean resolveLazily) {
		this.resolveLazily = resolveLazily;
	}
	@Override
	public boolean isMultipart(HttpServletRequest request) {
		return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
	}
	@Override
	public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
		return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
	}
	@Override
	public void cleanupMultipart(MultipartHttpServletRequest request) {
		if (!(request instanceof AbstractMultipartHttpServletRequest) ||
				((AbstractMultipartHttpServletRequest) request).isResolved()) {
			try {
				for (Part part : request.getParts()) {
					if (request.getFile(part.getName()) != null) {
						part.delete();
					}
				}
			}
			catch (Throwable ex) {
			}
		}
	}

}

这里满打满算就四个方法,其中一个还是 set 方法,我们来看另外三个功能性方法:

  1. isMultipart:这个方法主要是用来判断当前请求是不是文件上传请求,这里的判断思路很简单,就看请求的 content-type 是不是以 multipart/ 开头,如果是,则这就是一个文件上传请求,否则就不是文件上传请求。
  2. resolveMultipart:这个方法负责将当前请求封装一个 StandardMultipartHttpServletRequest 对象然后返回。
  3. cleanupMultipart:这个方法负责善后,主要完成了缓存的清理工作。

在这个过程中涉及到 StandardMultipartHttpServletRequest 对象,我们也来稍微说一下:

public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
		throws MultipartException {
	super(request);
	if (!lazyParsing) {
		parseRequest(request);
	}
}
private void parseRequest(HttpServletRequest request) {
	try {
		Collection<Part> parts = request.getParts();
		this.multipartParameterNames = new LinkedHashSet<>(parts.size());
		MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
		for (Part part : parts) {
			String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
			ContentDisposition disposition = ContentDisposition.parse(headerValue);
			String filename = disposition.getFilename();
			if (filename != null) {
				if (filename.startsWith("=?") && filename.endsWith("?=")) {
					filename = MimeDelegate.decode(filename);
				}
				files.add(part.getName(), new StandardMultipartFile(part, filename));
			}
			else {
				this.multipartParameterNames.add(part.getName());
			}
		}
		setMultipartFiles(files);
	}
	catch (Throwable ex) {
		handleParseFailure(ex);
	}
}

StandardMultipartHttpServletRequest 对象在构建的过程中,会自动进行请求解析,调用 getParts 方法获取所有的项,然后进行判断,将文件和普通参数分别保存下来备用。

这块的逻辑比较简单。

3.CommonsMultipartResolver

再来看 CommonsMultipartResolver。

先来看它的 isMultipart 方法:

@Override
public boolean isMultipart(HttpServletRequest request) {
	return ServletFileUpload.isMultipartContent(request);
}
public static final boolean isMultipartContent(
        HttpServletRequest request) {
    if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {
        return false;
    }
    return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
}

ServletFileUpload.isMultipartContent 方法其实就在我们引入的 commons-fileupload 包中。它的判断逻辑分两步:首先检查是不是 POST 请求,然后检查 content-type 是不是以 multipart/ 开始。

再来看它的 resolveMultipart 方法:

@Override
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
	if (this.resolveLazily) {
		return new DefaultMultipartHttpServletRequest(request) {
			@Override
			protected void initializeMultipart() {
				MultipartParsingResult parsingResult = parseRequest(request);
				setMultipartFiles(parsingResult.getMultipartFiles());
				setMultipartParameters(parsingResult.getMultipartParameters());
				setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
			}
		};
	}
	else {
		MultipartParsingResult parsingResult = parseRequest(request);
		return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
				parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
	}
}

根据 resolveLazily 属性值,选择两种不同的策略将当前对象重新构建成一个 DefaultMultipartHttpServletRequest 对象。如果 resolveLazily 为 true,则在 initializeMultipart 方法中进行请求解析,否则先解析,再构建 DefaultMultipartHttpServletRequest 对象。

具体的解析方法如下:

protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
	String encoding = determineEncoding(request);
	FileUpload fileUpload = prepareFileUpload(encoding);
	try {
		List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
		return parseFileItems(fileItems, encoding);
	}
	catch (FileUploadBase.SizeLimitExceededException ex) {
		//...
	}
}
protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {
	MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<>();
	Map<String, String[]> multipartParameters = new HashMap<>();
	Map<String, String> multipartParameterContentTypes = new HashMap<>();
	for (FileItem fileItem : fileItems) {
		if (fileItem.isFormField()) {
			String value;
			String partEncoding = determineEncoding(fileItem.getContentType(), encoding);
			try {
				value = fileItem.getString(partEncoding);
			}
			catch (UnsupportedEncodingException ex) {
				value = fileItem.getString();
			}
			String[] curParam = multipartParameters.get(fileItem.getFieldName());
			if (curParam == null) {
				multipartParameters.put(fileItem.getFieldName(), new String[] {value});
			}
			else {
				String[] newParam = StringUtils.addStringToArray(curParam, value);
				multipartParameters.put(fileItem.getFieldName(), newParam);
			}
			multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());
		}
		else {
			CommonsMultipartFile file = createMultipartFile(fileItem);
			multipartFiles.add(file.getName(), file);
		}
	}
	return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);
}

这里的解析就是首先获取到 FileItem 集合,然后调用 parseFileItems 方法进行进一步的解析。在进一步的解析中,会首先判断这是文件还是普通参数,如果是普通参数,则保存到 multipartParameters 中,具体保存过程中还会判断是否为数组,然后再将参数的 ContentType 保存到 multipartParameterContentTypes 中,文件则保存到 multipartFiles 中,最后由三个 Map 构成一个 MultipartParsingResult 对象并返回。

至此,StandardServletMultipartResolver 和 CommonsMultipartResolver 源码就和大家说完了,可以看到,还是比较容易的。

4.解析流程

最后,我们再来梳理一下解析流程。

以如下接口为例(因为在实际开发中一般都是通过如下方式上传文件):

@PostMapping("/upload")
public String fileUpload(MultipartFile file, HttpServletRequest req) {
    String format = sdf.format(new Date());
    String realPath = req.getServletContext().getRealPath("/img") + format;
    File folder = new File(realPath);
    if (!folder.exists()) {
        folder.mkdirs();
    }
    String oldName = file.getOriginalFilename();
    String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
    try {
        file.transferTo(new File(folder, newName));
        return req.getScheme() + "://" + req.getRemoteHost() + ":" + req.getServerPort() + "/img" + format + newName;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return "error";
}

这里 MultipartFile 对象主要就是在参数解析器中获取的,这里涉及到的参数解析器是 RequestParamMethodArgumentResolver。

在 RequestParamMethodArgumentResolver#resolveName 方法中有如下一行代码:

if (servletRequest != null) {
	Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
	if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
		return mpArg;
	}
}

这个方法会进行请求解析,返回 MultipartFile 对象或者 MultipartFile 数组。

@Nullable
public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request)
		throws Exception {
	MultipartHttpServletRequest multipartRequest =
			WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
	boolean isMultipart = (multipartRequest != null || isMultipartContent(request));
	if (MultipartFile.class == parameter.getNestedParameterType()) {
		if (!isMultipart) {
			return null;
		}
		if (multipartRequest == null) {
			multipartRequest = new StandardMultipartHttpServletRequest(request);
		}
		return multipartRequest.getFile(name);
	}
	else if (isMultipartFileCollection(parameter)) {
		if (!isMultipart) {
			return null;
		}
		if (multipartRequest == null) {
			multipartRequest = new StandardMultipartHttpServletRequest(request);
		}
		List<MultipartFile> files = multipartRequest.getFiles(name);
		return (!files.isEmpty() ? files : null);
	}
	else if (isMultipartFileArray(parameter)) {
		if (!isMultipart) {
			return null;
		}
		if (multipartRequest == null) {
			multipartRequest = new StandardMultipartHttpServletRequest(request);
		}
		List<MultipartFile> files = multipartRequest.getFiles(name);
		return (!files.isEmpty() ? files.toArray(new MultipartFile[0]) : null);
	}
	else if (Part.class == parameter.getNestedParameterType()) {
		if (!isMultipart) {
			return null;
		}
		return request.getPart(name);
	}
	else if (isPartCollection(parameter)) {
		if (!isMultipart) {
			return null;
		}
		List<Part> parts = resolvePartList(request, name);
		return (!parts.isEmpty() ? parts : null);
	}
	else if (isPartArray(parameter)) {
		if (!isMultipart) {
			return null;
		}
		List<Part> parts = resolvePartList(request, name);
		return (!parts.isEmpty() ? parts.toArray(new Part[0]) : null);
	}
	else {
		return UNRESOLVABLE;
	}
}

首先获取 multipartRequest 对象,然后再从中获取文件或者文件数组。如果我们使用 StandardServletMultipartResolver 做文件上传,这里获取到的 multipartRequest 就是 StandardMultipartHttpServletRequest;如果我们使用 CommonsMultipartResolver 做文件上传,这里获取到的 multipartRequest 就是 DefaultMultipartHttpServletRequest。

2021最新完整面试题及答案(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、JVM、源码、算法等详细讲解,详细的学习规划图等学习资料,私信我:Java 获取

最开始是为了解决图片保存到MySQL数据库的问题,之前没做过,后来自己查资料。找到两种有效的方式。

第一种是直接把图片转化成字节流存进数据库,这样的例子很多,好处很明显,存进去以后,可以直接备份数据库带走,简单方面,坏处就是图片占用内存,一旦数量上来,那么对内存库的压力很大,在高并发访问的情况下,占用资源较大,所以一般情况下,很多公司都不是采用这样的方式。而是采用第二种,把图片上传到服务器的文件夹内,然后数据库直接存储图片的地址,这样的坏处就是项目的迁移,文件夹也要跟着备份,备份两次,好处就很明显了,显然存地址跟存内容占用的空间大小就很明显,大大减轻了数据库的压力。因此本文采用的就是数据库存储图片地址,图片直接存储在本机文件夹中。

正文

营业执照选择自营,必须填写营业执照代码跟照片,非自营的,可以不用填写。代码如下:

<tr>

<td style="text-align: right;"><font size="2px">营业执照代码</font></td>

<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="radio" name="ra" id="ra" checked="true" onclick="_change(this);" value="a"/> 自营

<input type="radio" name="ra" id="ra" onclick="_change(this); "value="b"/> 非自营

<td><font color="#999999">* 自营必填,同时上传营业执照照片</font></td>

</td> <div id="prvid"></div>

</tr>

上传照片在form表单里必须填写 enctype="multipart/form-data",否则图片就无法上传,只能上传文字信息,但是加了这条语句以后,后台就无法用request.getParameter(arg0)来获取参数了,因为上传的信息全部转化成了字节流,这是我添加以后一直出错发现的问题所在。

接下来我们需要编写一个upload的类来处理图片的上传,这个类是网上找的,需要添加包

cos.jar,下载地址稍后会在留言里。代码如下

import java.io.File;

import java.io.IOException;

import java.util.Date;

import java.util.HashMap;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.oreilly.servlet.multipart.FilePart;

import com.oreilly.servlet.multipart.MultipartParser;

import com.oreilly.servlet.multipart.ParamPart;

import com.oreilly.servlet.multipart.Part;

public class Upload {

public static Map<String, String> upload(HttpServletRequest request,

int maxSize, String path) {

//以map形式保存数据 key对应保存的是获取界面上的name名称 value保存的是获取界面上的name对应的值

Map<String, String> map = new HashMap<String, String>();

Part part = null;

try {

MultipartParser mrequest = new MultipartParser(request, maxSize);

mrequest.setEncoding("utf-8");

//遍历所有的part组

while ((part = mrequest.readNextPart()) != null) {

if (part.isFile()) { //判断是否是文件

FilePart filepart = (FilePart) part;//转化成文件组

String fileName = filepart.getFileName();//得到文件名

if (fileName != null && fileName.length() > 0) {

// 取得扩展名

String fileExtName = fileName.substring(

fileName.lastIndexOf(".") + 1).toLowerCase();

// 只上传图片 //判断图片上传的格式是否符合 后缀名是否有效

if (fileExtName.equalsIgnoreCase("jpeg")

|| fileExtName.equalsIgnoreCase("png")||

fileExtName.equalsIgnoreCase("jpg")

|| fileExtName.equalsIgnoreCase("gif")

|| fileExtName.equalsIgnoreCase("ico")

|| fileExtName.equalsIgnoreCase("bmp")

|| fileExtName.equalsIgnoreCase("flv")

|| fileExtName.equalsIgnoreCase("mp4")

|| fileExtName.equalsIgnoreCase("mp3")) {

/*String newFileName = new Date().getTime() + "."+ fileExtName;//重新改文件名 文件名+扩展名 */

String newFileName =new Date().getTime() +fileName;//不改图片名字

String newPath = path + "/" + newFileName; //文件处理文件上传的路径

File newFile = new File(newPath);

filepart.writeTo(newFile); //将文件真正写入到对应的文件夹中

//filepart.getName() 得到 request 要接收的参数的名字

map.put("newFileName", newFileName);

map.put(filepart.getName(), newFileName);//把文件信息保存到map中

map.put("newFile", newFile.toString());

} else {

map.put("geshi", "geshi");

continue;

}// 说明上传的不是图片

} else {

map.put("yes","yes");

continue; // 说明没有选择上传图片

}

} else if (part.isParam()) { //判断是否是参数

ParamPart paramPart = (ParamPart) part;

map.put(paramPart.getName(), paramPart.getStringValue());

}

}

} catch (IOException e) {

e.printStackTrace();

}

return map;

}

}

else if (part.isParam()) { //判断是否是参数

ParamPart paramPart = (ParamPart) part;

map.put(paramPart.getName(), paramPart.getStringValue());

}

通过观察代码,熟悉思路,大概就是先看这个part是否是文件,如果是文件,则进行一系列文件处理,如果不是文件,看是否是参数,如果是参数,则依次用map存储起来,因此在后台就可以map.get()来获取参数了,实验以后发现确实可行,因此后台代码如下:

后台servlet代码:

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

// TODO Auto-generated method stub

request.setCharacterEncoding("UTF-8");

String PATH = "image/license_pic";

String path = "E:/"+PATH;

Map<String, String> map = Upload.upload(request, 1024 * 1024 * 10, path);

// String newfile = map.get("newFile");

String newFileName =PATH+ map.get("newFileName");

User user = new User();

Shop shop = new Shop();

user.setUsername(map.get("username"));

user.setPassword(map.get("password"));

shop.setName(map.get("name"));

shop.setAddress(map.get("address"));

shop.setYear(map.get("year"));

shop.setPhone(map.get("phone"));

shop.setMaster(map.get("master"));

shop.setIdentity(map.get("identity"));

shop.setLicense(map.get("license"));

shop.setTelephone(map.get("telephone"));

shop.setNote(map.get("note"));

shop.setLicense_pic(newFileName);

System.out.println(newFileName);

UserServiceImpl us = new UserServiceImpl();

ShopServiceImpl ss = new ShopServiceImpl();

try {

int i = 0, j = 0;

i = us.insertUser(user);

System.out.println(user.getUsername());

int id = us.selectByName(user.getUsername()).getId();

j = ss.insertShop(shop, id);

if (i == 1 && j == 1) {

System.out.println("插入成功!");

request.getSession().setAttribute("u", user);

request.getRequestDispatcher("/admin/login/login.jsp").forward(request, response);

} else {

System.out.println("插入失败!");

response.sendRedirect("/shop_Register3/admin/login/register.jsp?error=yes");

}

} catch (Exception e) {

e.printStackTrace();

}

}

请点击此处输入图片描最后得到结果输出,输出显示文件名称,然后用户名,最后插入成功,说明是可行的,查看自己的E盘,确实看到照片已经上传成功了,这是一个大概的步骤,具体代码因为项目代码多,就不全部上传了,有问题可以留言。

文实例为大家分享了JSP+Servlet实现文件上传到服务器功能的具体代码,供大家参考,具体内容如下

项目目录结构大致如下:

正如我在上图红线画的三个东西:Dao、service、servlet 这三层是主要的结构,类似 MVC 架构,Dao是模型实体类(逻辑层),service是服务层,servlet是视图层,三者协作共同完成项目。

这里的User是由user表来定义的一个类,再封装增删改查等操作,实现从数据库查询与插入,修改与删除等操作,并实现了分页操作,也实现了将图片放到服务器上运行的效果。

Dao层:主要实现了User类的定义,接口IUserDao的定义与实现(UserDaoImpl);

service层:直接定义一个接口类IUserService,与IUserDao相似,再实现其接口类UserServiceImpl,直接实例化UserDaoImpl再调用其方法来实现自己的方法,重用了代码。详见代码吧;

servlet层:起初是将表User 的每个操作方法都定义成一个servlet 去实现,虽然简单,但是太多了,不好管理,于是利用 基类BaseServlet 实现了“反射机制”,通过获取的 action 参数自己智能地调用对应的方法,而UserServlet则具体实现自己的方法,以供调用,方便许多,详见之前的博文或下述代码。

将文件上传到 tomcat 服务器的编译后运行的过程的某个文件关键要在每次编译后手动为其创建该文件夹来存放相应的上传文件,否则会导致每次重启 tomcat 服务器后该编译后的工程覆盖了原先的,导致上传文件存放的文件夹不存在,导致代码找不到该文件夹而报错,即上传不成功。如下图所示:

主要是考虑图片路径的问题,手工设置路径肯定不能保证不重复,所以取到上传图片的后缀名后利用随机生成的随机数作为图片名,这样就不会重复名字了:

String extendedName = picturePath.substring(picturePath.lastIndexOf("."),// 截取从最后一个'.'到字符串结束的子串。

picturePath.length());

// 把文件名称重命名为全球唯一的文件名

String uniqueName = UUID.randomUUID().toString();

saveFileName = uniqueName + extendedName;// 拼接路径名

增加用户时代码如下:

// 增

public void add(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

System.out.println("add方法被调用");

// 获取数据

int id = 0;

String username = null;

String password = null;

String sex = null;

Date birthday = null;

String address = null;

String saveFileName = null;

String picturePath = null;

// 得到表单是否以enctype="multipart/form-data"方式提交

boolean isMulti = ServletFileUpload.isMultipartContent(request);

if (isMulti) {

// 通过FileItemFactory得到文件上传的对象

FileItemFactory fif = new DiskFileItemFactory();

ServletFileUpload upload = new ServletFileUpload(fif);

try {

List<FileItem> items = upload.parseRequest(request);

for (FileItem item : items) {

// 判断是否是普通表单控件,或者是文件上传表单控件

boolean isForm = item.isFormField();

if (isForm) {// 是普通表单控件

String name = item.getFieldName();

if ("id".equals(name)) {

id = Integer.parseInt(item.getString("utf-8"));

System.out.println(id);

}

if ("sex".equals(name)) {

sex = item.getString("utf-8");

System.out.println(sex);

}

if ("username".equals(name)) {

username = item.getString("utf-8");

System.out.println(username);

}

if ("password".equals(name)) {

password = item.getString("utf-8");

System.out.println(password);

}

if ("birthday".equals(name)) {

String birthdayStr = item.getString("utf-8");

SimpleDateFormat sdf = new SimpleDateFormat(

"yyyy-MM-dd");

try {

birthday = sdf.parse(birthdayStr);

} catch (ParseException e) {

e.printStackTrace();

}

System.out.println(birthday);

}

if ("address".equals(name)) {

address = item.getString("utf-8");

System.out.println(address);

}

if ("picturePath".equals(name)) {

picturePath = item.getString("utf-8");

System.out.println(picturePath);

}

} else {// 是文件上传表单控件

// 得到文件名 xxx.jpg

String sourceFileName = item.getName();

// 得到文件名的扩展名:.jpg

String extendedName = sourceFileName.substring(

sourceFileName.lastIndexOf("."),

sourceFileName.length());

// 把文件名称重命名为全球唯一的文件名

String uniqueName = UUID.randomUUID().toString();

saveFileName = uniqueName + extendedName;

// 得到上传到服务器上的文件路径

// C:\apache-tomcat-7.0.47\webapps\taobaoServlet4\upload\xx.jpg

String uploadFilePath = request.getSession()

.getServletContext().getRealPath("upload/");

File saveFile = new File(uploadFilePath, saveFileName);

// 把保存的文件写出到服务器硬盘上

try {

item.write(saveFile);

} catch (Exception e) {

e.printStackTrace();

}

}

}

} catch (NumberFormatException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (FileUploadException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

// 2、封装数据

User user = new User(id, username, password, sex, birthday, address,

saveFileName);

// 3、调用逻辑层API

IUserService iUserService = new UserServiceImpl();

// 4、控制跳转

HttpSession session = request.getSession();

if (iUserService.save(user) > 0) {

System.out.println("添加新用户成功!");

List<User> users = new ArrayList<User>();

users = iUserService.listAll();

session.setAttribute("users", users);

response.sendRedirect("UserServlet?action=getPage");

} else {

System.out.println("添加新用户失败!");

PrintWriter out = response.getWriter();

out.print("<script type='text/javascript'>");

out.print("alert('添加新用户失败!请重试!');");

out.print("</script>");

}

}

修改用户时注意考虑图片更改和没更改这两种情况,图片更改时要先获取原图片并删除其在服务器上的图片,再添加新图片到服务器;图片不更改时则无需更新图片路径。

// 改

public void update(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

System.out.println("update方法被调用");

HttpSession session = request.getSession();

// 获取数据

int id = (int)session.getAttribute("id");

String username = null;

String password = null;

String sex = null;

Date birthday = null;

String address = null;

String saveFileName = null;

String picturePath = null;

IUserService iUserService = new UserServiceImpl();

// 得到表单是否以enctype="multipart/form-data"方式提交

boolean isMulti = ServletFileUpload.isMultipartContent(request);

if (isMulti) {

// 通过FileItemFactory得到文件上传的对象

FileItemFactory fif = new DiskFileItemFactory();

ServletFileUpload upload = new ServletFileUpload(fif);

try {

List<FileItem> items = upload.parseRequest(request);

for (FileItem item : items) {

// 判断是否是普通表单控件,或者是文件上传表单控件

boolean isForm = item.isFormField();

if (isForm) {// 是普通表单控件

String name = item.getFieldName();

if ("sex".equals(name)) {

sex = item.getString("utf-8");

System.out.println(sex);

}

if ("username".equals(name)) {

username = item.getString("utf-8");

System.out.println(username);

}

if ("password".equals(name)) {

password = item.getString("utf-8");

System.out.println(password);

}

if ("birthday".equals(name)) {

String birthdayStr = item.getString("utf-8");

SimpleDateFormat sdf = new SimpleDateFormat(

"yyyy-MM-dd");

try {

birthday = sdf.parse(birthdayStr);

} catch (ParseException e) {

e.printStackTrace();

}

System.out.println(birthday);

}

if ("address".equals(name)) {

address = item.getString("utf-8");

System.out.println(address);

}

if ("picturePath".equals(name)) {

picturePath = item.getString("utf-8");

System.out.println(picturePath);

}

} else {// 是文件上传表单控件

// 得到文件名 xxx.jpg

picturePath = item.getName();

if (picturePath != "") {// 有选择要上传的图片

// 得到文件名的扩展名:.jpg

String extendedName = picturePath.substring(

picturePath.lastIndexOf("."),// 截取从最后一个'.'到字符串结束的子串。

picturePath.length());

// 把文件名称重命名为全球唯一的文件名

String uniqueName = UUID.randomUUID().toString();

saveFileName = uniqueName + extendedName;// 拼接路径名

// 得到上传到服务器上的文件路径

// C:\apache-tomcat-7.0.47\webapps\CommonhelloWorldServlet\upload\xx.jpg

String uploadFilePath = request.getSession()

.getServletContext().getRealPath("upload/");

File saveFile = new File(uploadFilePath,

saveFileName);

// 把保存的文件写出到服务器硬盘上

try {

item.write(saveFile);

} catch (Exception e) {

e.printStackTrace();

}

// 3、调用逻辑层 API

// 根据id查询用户并获取其之前的图片

User user = iUserService.getUserById(id);

String oldPic = user.getPicturePath();

String oldPicPath = uploadFilePath + "\" + oldPic;

File oldPicTodelete = new File(oldPicPath);

oldPicTodelete.delete();// 删除旧图片

}

}

}

} catch (NumberFormatException e) {

e.printStackTrace();

} catch (FileUploadException e) {

e.printStackTrace();

}

}

System.out.println(id + "\t" + username + "\t" + password + "\t" + sex

+ "\t" + address + "\t" + picturePath + "\t" + birthday);

// 2、封装数据

User user = new User(id, username, password, sex, birthday, address,

saveFileName);

if (iUserService.update(user) > 0) {

System.out.println("修改数据成功!");

List<User> users = new ArrayList<User>();

users = iUserService.listAll();

session.setAttribute("users", users);

// 4、控制跳转

response.sendRedirect("UserServlet?action=getPage");

} else {

System.out.println("修改数据失败!");

PrintWriter out = response.getWriter();

out.print("<script type='text/javascript'>");

out.print("alert('修改数据失败!请重试!');");

out.print("</script>");

}

}

删除的话就比较简单了,直接获取原图片路径并删除,则原图片在服务器上被删除。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。