我有一个表单,允许用户输入几个字段并上传两个单独的文件。在上传每个文件时,都会向服务器发送一个请求以检索文件并构建显示。一旦用户对显示看起来不错感到满意,他们就可以将新数据提交到数据库。
我遇到的问题是 Spring-MVC 似乎在最后一次调用服务器时丢失了会话对象。我需要它来保留会话,直到我明确告诉它使用 SessionStatus 删除对象。
这是我的控制器类的代码:
package com.mbn.modeldisplay.controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import com.mbn.modeldisplay.pojo.CandidateModel;
import com.mbn.modeldisplay.pojo.Model;
import com.mbn.modeldisplay.service.IModelService;
import com.mbn.modeldisplay.service.ServiceException;
import com.mbn.modeldisplay.util.TempFileManager;
@SessionAttributes(value = { "candidateModel" })
@Controller
@RequestMapping("/model/*")
public class ModelHandler {
protected static Logger log = LoggerFactory.getLogger(ModelHandler.class);
protected IModelService modelService = null;
private static final String DEFAULT_TEMPLATE_TEXTURE = "/assets/defaultimage.png";
private static final String CANDIDATE_MODEL_NAME = "candidateModel";
@ModelAttribute(CANDIDATE_MODEL_NAME)
public CandidateModel createCandidate() {
CandidateModel candidate = new CandidateModel();
log.debug("createCandidate returning: " + candidate);
log.debug("\n\n");
return candidate;
}
@RequestMapping(method = RequestMethod.GET)
public String startEditModel(
@RequestParam(value = "modelId", required = false) String modelId,
@ModelAttribute CandidateModel candidate) {
try {
Model m = modelService.read(modelId);
if (null == m) {
m = new Model();
m.modelId = modelId;
}
candidate.setOriginal(m);
log.debug("startEditModel returning: " + candidate);
} catch (ServiceException se) {
log.error("Unable to access datastore", se);
}
log.debug("\n\n");
return "ModelEdit";
}
@RequestMapping(value = "register/binary", method = RequestMethod.POST)
public String addModel(@RequestParam MultipartFile model,
@ModelAttribute(CANDIDATE_MODEL_NAME) CandidateModel tempCandidate) {
log.debug("addModel received: " + tempCandidate);
try {
File tempFile = tempCandidate.getCandidateModelFile();
if (null != tempFile) {
log.debug("Remove previous candidate: "
+ tempFile.getAbsolutePath());
tempFile.delete();
}
tempFile = TempFileManager.getTempFile(
"norrim01",
"ModelDisplay",
model.getOriginalFilename().substring(
model.getOriginalFilename().lastIndexOf('.')));
log.debug("Writing to temp file: " + tempFile.getAbsolutePath());
InputStream is = model.getInputStream();
FileOutputStream fw = new FileOutputStream(tempFile);
byte[] buffer = new byte[1024];
int read;
while (-1 != (read = is.read(buffer))) {
fw.write(buffer, 0, read);
}
fw.flush();
fw.close();
tempCandidate.setCandidateModelFile(tempFile);
} catch (Exception e) {
log.error("Unable to process model file candidate.", e);
return "ErrorPage";
}
log.debug("\n\n");
return "ModelEdit";
}
@RequestMapping(value = "register/binary", method = RequestMethod.GET)
public void getCandidateModel(HttpServletResponse response,
@ModelAttribute(CANDIDATE_MODEL_NAME) CandidateModel tempCandidate)
throws Exception {
log.debug("getCandidateModel received: " + tempCandidate);
File tempFile = tempCandidate.getCandidateModelFile();
if (null == tempFile || !tempFile.exists()) {
throw new FileNotFoundException("Candidate Model File");
}
log.debug("register/binary returning " + tempFile.getAbsolutePath());
response.setContentType("application/octet-stream");
response.setHeader(
"Content-Disposition",
"attachment; filename=candidatemodel"
+ tempFile.getName().substring(
tempFile.getName().lastIndexOf('.')));
InputStream is = new FileInputStream(tempFile);
OutputStream fw = response.getOutputStream();
byte[] buffer = new byte[1024];
int read;
while (-1 != (read = is.read(buffer))) {
fw.write(buffer, 0, read);
}
log.debug("\n\n");
fw.flush();
}
@RequestMapping(value = "register/template", method = RequestMethod.POST)
public String addTemplate(@RequestParam MultipartFile template,
@ModelAttribute(CANDIDATE_MODEL_NAME) CandidateModel tempCandidate)
throws Exception {
log.debug("addTemplate received: " + tempCandidate);
try {
File tempFile = tempCandidate.getCandidateTemplateFile();
if (null != tempFile) {
log.debug("Remove previous candidate: "
+ tempFile.getAbsolutePath());
tempFile.delete();
}
tempFile = TempFileManager.getTempFile(
"norrim01",
"ModelDisplay",
template.getOriginalFilename().substring(
template.getOriginalFilename().lastIndexOf('.')));
log.debug("Writing to temp file: " + tempFile.getAbsolutePath());
InputStream is = template.getInputStream();
FileOutputStream fw = new FileOutputStream(tempFile);
byte[] buffer = new byte[1024];
int read;
while (-1 != (read = is.read(buffer))) {
fw.write(buffer, 0, read);
}
fw.flush();
fw.close();
tempCandidate.setCandidateTemplateFile(tempFile);
} catch (Exception e) {
log.error("Unable to process model file candidate.", e);
return "ErrorPage";
}
log.debug("\n\n");
return "ModelEdit";
}
@RequestMapping(value = "register/template", method = RequestMethod.GET)
public void getCandidateTemplate(
@ModelAttribute(CANDIDATE_MODEL_NAME) CandidateModel tempCandidate,
HttpSession session, HttpServletResponse response) throws Exception {
log.debug("getCandidateTemplate received: " + tempCandidate);
File tempFile = tempCandidate.getCandidateTemplateFile();
log.debug("register/template found in session: " + tempFile);
if (null == tempFile || !tempFile.exists()) {
String location = session.getServletContext().getRealPath(
DEFAULT_TEMPLATE_TEXTURE);
if (null != location)
tempFile = new File(location);
else
throw new FileNotFoundException(
"Unable to locate the default template texture");
}
log.debug("register/template returning " + tempFile.getAbsolutePath());
response.setContentType("application/octet-stream");
response.setHeader(
"Content-Disposition",
"attachment; filename=candidatetemplate"
+ tempFile.getName().substring(
tempFile.getName().lastIndexOf('.')));
InputStream is = new FileInputStream(tempFile);
OutputStream fw = response.getOutputStream();
byte[] buffer = new byte[1024];
int read;
while (-1 != (read = is.read(buffer))) {
fw.write(buffer, 0, read);
}
fw.flush();
log.debug("\n\n");
}
/**
* @return the modelService
*/
public IModelService getModelService() {
return modelService;
}
/**
* @param modelService
* the modelService to set
*/
@Autowired
public void setModelService(IModelService modelService) {
this.modelService = modelService;
}
}
模型文件上传并使用 GET 请求正确检索到相同的 URL,图形文件也正确上传到/register/template
URL 并添加到 CandidateModel 会话对象,但是当register/template
发出 GET 时,Spring-MVC 会创建一个新的,空 CandidateModel 对象,我无法检索上传的图形,从调试输出中可以看出,这里:
2013-09-11 11:58:10,078 DEBUG [ModelHandler] addModel received: CandidateModel [original=null, candidateModelFile=null, candidateTemplateFile=null, object Id=com.mbn.modeldisplay.pojo.CandidateModel@1c6cc9c]
2013-09-11 11:58:10,216 DEBUG [ModelHandler] Writing to temp file: C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim011291263503728127085.obj
2013-09-11 11:58:10,221 DEBUG [ModelHandler]
2013-09-11 11:58:10,398 DEBUG [ModelHandler] getCandidateModel received: CandidateModel [original=null, candidateModelFile=C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim011291263503728127085.obj, candidateTemplateFile=null, object Id=com.mbn.modeldisplay.pojo.CandidateModel@1c6cc9c]
2013-09-11 11:58:10,398 DEBUG [ModelHandler] register/binary returning C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim011291263503728127085.obj
2013-09-11 11:58:10,400 DEBUG [ModelHandler]
2013-09-11 11:58:10,460 DEBUG [ModelHandler] createCandidate returning: CandidateModel [original=null, candidateModelFile=null, candidateTemplateFile=null, object Id=com.mbn.modeldisplay.pojo.CandidateModel@3c7038b9]
2013-09-11 11:58:10,460 DEBUG [ModelHandler]
2013-09-11 11:58:10,463 DEBUG [ModelHandler] getCandidateTemplate received: CandidateModel [original=null, candidateModelFile=null, candidateTemplateFile=null, object Id=com.mbn.modeldisplay.pojo.CandidateModel@3c7038b9]
2013-09-11 11:58:10,463 DEBUG [ModelHandler] register/template found in session: null
2013-09-11 11:58:10,464 DEBUG [ModelHandler] register/template returning C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\webapps\ModelDisplay\assets\defaultimage.png
2013-09-11 11:58:10,465 DEBUG [ModelHandler]
2013-09-11 11:58:15,426 DEBUG [ModelHandler] addTemplate received: CandidateModel [original=null, candidateModelFile=C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim011291263503728127085.obj, candidateTemplateFile=null, object Id=com.mbn.modeldisplay.pojo.CandidateModel@1c6cc9c]
2013-09-11 11:58:15,604 DEBUG [ModelHandler] Writing to temp file: C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim01647051706271088553.PNG
2013-09-11 11:58:15,986 DEBUG [ModelHandler]
2013-09-11 11:58:16,014 DEBUG [ModelHandler] getCandidateModel received: CandidateModel [original=null, candidateModelFile=C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim011291263503728127085.obj, candidateTemplateFile=C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim01647051706271088553.PNG, object Id=com.mbn.modeldisplay.pojo.CandidateModel@1c6cc9c]
2013-09-11 11:58:16,015 DEBUG [ModelHandler] register/binary returning C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\temp\norrim011291263503728127085.obj
2013-09-11 11:58:16,016 DEBUG [ModelHandler]
2013-09-11 11:58:16,117 DEBUG [ModelHandler] createCandidate returning: CandidateModel [original=null, candidateModelFile=null, candidateTemplateFile=null, object Id=com.mbn.modeldisplay.pojo.CandidateModel@1ae73783]
2013-09-11 11:58:16,118 DEBUG [ModelHandler]
2013-09-11 11:58:16,119 DEBUG [ModelHandler] getCandidateTemplate received: CandidateModel [original=null, candidateModelFile=null, candidateTemplateFile=null, object Id=com.mbn.modeldisplay.pojo.CandidateModel@1ae73783]
2013-09-11 11:58:16,119 DEBUG [ModelHandler] register/template found in session: null
2013-09-11 11:58:16,119 DEBUG [ModelHandler] register/template returning C:\Users\Michael Norris\Development\Tomcat\apache-tomcat-7.0.29\webapps\ModelDisplay\assets\defaultimage.png
2013-09-11 11:58:16,121 DEBUG [ModelHandler]
我的观点是使用 JQuery 和 Ajax 在文件被选中后立即发送文件。如果结果成功,showCandidateModel
则使用 THREE.js 中的例程查询服务器以检索文件。以下是选择文件后立即发生的上传示例:
if (formdata) {
$.ajax({
url: "/ModelDisplay/rest/model/register/binary",
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function (res) {
showCandidateModel();
},
error: function (jqXHR, textStatus, errorThrown) {
document.getElementById("response").innerHTML =
"<h2>" + textStatus + "</h2><BR><BR><p>" + errorThrown + "<p>";
}
});
}
我还尝试将对象添加到处理程序方法并直接使用andHttpSession
与之交互,但它表现出相同的行为,让我相信 Spring-MVC 实际上并没有使用来自 Tomcat 的真实会话对象,而是使用它的其他一些实现管理。addAttribute
getAttribute
为了完整起见,这是我的 Spring 配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- the application context definition for the springapp DispatcherServlet -->
<bean id="mongoDBDatabaseName" class="java.lang.String">
<constructor-arg value="ModelDisplay"/>
</bean>
<bean id="mongoDBDatabaseHost" class="java.lang.String">
<constructor-arg value="localhost"/>
</bean>
<bean id="mongoDBDatabasePort" class="java.lang.Integer">
<constructor-arg value="27017"/>
</bean>
<bean id="mongoDB" class="com.mbn.modeldisplay.dao.ModelDisplayMongoDB">
<constructor-arg ref="mongoDBDatabaseName"/>
<constructor-arg ref="mongoDBDatabaseHost"/>
<constructor-arg ref="mongoDBDatabasePort"/>
</bean>
<context:component-scan base-package="com.mbn.modeldisplay"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="100000000"/>
</bean>
</beans>
这是我的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
我已经搜索了示例,我发现许多示例显示了单个文件的上传和多个文件的上传示例,但没有一个示例提供多个文件的多步上传。在此先感谢您的时间。