Java for Web学习笔记(六五):Controller替代Servlet(7)上传和下载(自定义View)

来源:互联网 发布:双十一淘宝销售额 编辑:程序博客网 时间:2024/05/16 08:37

文件上传

文件上传使用的multipart,在之前介绍过。这里我们不采用xml配置,采用代码配置。之前我们采用的resolver为org.springframework.web.multipart.commons.CommonsMultipartResolver,API解析org.springframework.web.multipart.commons包是MultipartResolver implementation for Apache Commons FileUpload。本学习采用Servlet 3.1内置的multipart的支持,采用org.springframework.web.multipart.support.StandardServletMultipartResolver,这是Standard implementation of the MultipartResolver interface, based on the Servlet 3.0 Part API,因此我们无需引入commons-fileupload。

代码配置

(1) BootStrap:允许被设置multipart参数

public class BootStrap implements WebApplicationInitializer{    @Override    public void onStartup(ServletContext container) throws ServletException {        container.getServletRegistration("default").addMapping("/resource/*");        AnnotationConfigWebApplicationContext rootContext =  new AnnotationConfigWebApplicationContext();        rootContext.register(RootContextConfiguration.class);        container.addListener(new ContextLoaderListener(rootContext));        AnnotationConfigWebApplicationContext servletContext =  new AnnotationConfigWebApplicationContext();        servletContext.register(ServletContextConfiguration.class);        ServletRegistration.Dynamic dispatcher = container.addServlet("springDispatcher", new DispatcherServlet(servletContext));        dispatcher.setLoadOnStartup(1);        // 【1】在DispatcherServlet中允许并设置multipart的参数。        dispatcher.setMultipartConfig(new MultipartConfigElement(            null /*location*/, 20_971_520L/*maxFileSize*/, 62_914_560L/*maxRequestSize*/, 512_000/*fileSizeThreshold*/));        dispatcher.addMapping("/");       // 【注】这里虽然和上传下载没有关系,但是如果我们采用代码设置配置,BootStrap是在web启动时运行,我们最好把所有的初始化的处理放在这里,而不是将其中一部分放入ServletContextListener,尽可能不要将初始化代码分在两个不同的地方。但是我们也注意到,WebApplicationInitializer是不提供web app退出的处理,所以ServletContextListener仍可能要用上。          initFilter(container);    }    private void initFilter(ServletContext container){        ... ...    } }

(2) ServletContextConfiguration:设置multipart resolver

@Configuration@ComponentScan(basePackages = "cn.wei.flowingflying.customer_support.site",               useDefaultFilters = false,               includeFilters = @ComponentScan.Filter(Controller.class))public class ServletContextConfiguration {    @Bean    public ViewResolver viewResolver(){        InternalResourceViewResolver resolver = new InternalResourceViewResolver();                resolver.setViewClass(JstlView.class);         resolver.setPrefix("/WEB-INF/jsp/view/");        resolver.setSuffix(".jsp");        return resolver;    }    @Bean    public RequestToViewNameTranslator viewNameTranslator(){        return new DefaultRequestToViewNameTranslator();    }    //【2】Spring兼容旧的Servlet API。在Servlet3.0之前,并没有内置multipart的支持,需使用第三方,为兼容,Spring需要设置MultipartResolver来明确采用内置的还是第三方的。    // 例子采用了自带的org.springframework.web.multipart.support.StandardServletMultipartResolver,不引入第三方。    @Bean    public MultipartResolver multipartResolver() {        return new StandardServletMultipartResolver();    }}

jsp文件:add.jsp片段

既然学习了spring的form tag,我们就例子中使用。但是没有form:file,我们仍需要使用<input>的方式。


<form:form method="post" enctype="multipart/form-data" modelAttribute="ticketForm">            <form:label path="subject">Subject</form:label><br />    <form:input path="subject"/><br /><br />    <form:label path="body">Body</form:label><br />    <form:textarea path="body" rows="5" cols="30" /><br /><br />    <b>Attachments</b><br />    <input type="file" name="attachments" multiple="multiple"/><br /><br />    <input type="submit" value="Submit"/></form:form>
对应的java类Form
public class Form{    private String subject;    private String body;    private List<MultipartFile> attachments;    ... getters & setters ...}

Controller的代码片段

@RequestMapping(value = "create", method = RequestMethod.GET)public String create(Map<String, Object> model){    model.put("ticketForm", new TicketController.Form());    return "ticket/add";}/* 1、ticket是业务对象,form是Form对象(输入对象),这两者有关联,但通常不一样。我们再三强调,我们应将业务逻辑和用户UI逻辑分离 * 2、从session中获取用户的登录账号 * 3、采用spring form tag可以将form对象和页面呈现进行双向转换,对于页面转为form对象,使用普通的input并无影响 */@RequestMapping(value = "create", method = RequestMethod.POST)public View create(HttpSession session,Form form) throws IOException{    Ticket ticket = new Ticket();    ticket.setId(getNextTicketId());    ticket.setCustomerName((String)session.getAttribute("username"));    ticket.setSubject(form.getSubject());    ticket.setBody(form.getBody());    ticket.setDateCreated(Instant.now());    for(MultipartFile filePart : form.getAttachments()){        logger.debug("Processing attachment for new ticket.");        Attachment attachment = new Attachment();        attachment.setName(filePart.getOriginalFilename());        attachment.setContents(filePart.getBytes());        attachment.setMimeContentType(filePart.getContentType());        if(attachment.getName()!= null && attachment.getName().length() > 0                && attachment.getContents() != null && attachment.getContents().length > 0){            ticket.addAttachment(attachment);        }    }    this.ticketDatabase.put(ticket.getId(), ticket);    return new RedirectView("/ticket/view/" + ticket.getId(),true,false);}

文件下载

文件下载使用自定义的view来进行处理。

自定义下载view:DownloadingView

public class DownloadingView implements View{    private final String filename;    private final String contentType;    private final byte[] contents;    public DownloadingView(String filename, String contentType,byte[] contents){        this.filename = filename;        this.contentType = contentType;        this.contents = contents;    }    @Override    public String getContentType() {        return this.contentType;    }    // render很有意思,参数包括servlet的request和response,以及controller的处理结果的数据存放model,可以根据model构造返回的http响应。    @Override    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)            throws Exception {        response.setHeader("Content-Disposition", "attachment; filename=" + this.filename);        response.setContentType("application/octet-stream");        ServletOutputStream stream = response.getOutputStream();        stream.write(this.contents);            }}

Controller的代码片段

// 1、在url中采用正则表达式,.表示匹配除“\r\n”之外的任何单个字符;+表示匹配前面的子表达式一次或多次(大于等于1次)// 2、判断如果{ticketId}无效,则跳转;进而如果{attachment}无效则跳转;最后进入自定义的view进行处理。// 3、自定义的View在构造函数中已经填入所需的信息,不采用model进行数据传递,因此不使用ModelAndView,采用返回View的方式@RequestMapping( value = "/{ticketId}/attachment/{attachment:.+}",                 method = RequestMethod.GET )public View download(@PathVariable("ticketId") long ticketId,                     @PathVariable("attachment") String name){    Ticket ticket = ticketDatabase.get(ticketId);    if(ticket == null)        return getListRedirectView();    Attachment attachment = ticket.getAttachment(name);    if(attachment == null){        logger.info("Requested attachment {} not found on ticket {}.", name, ticketId);        return this.getListRedirectView();    }    return new DownloadingView(attachment.getName(),attachment.getMimeContentType(), attachment.getContents());}

相关链接: 我的Professional Java for Web Applications相关文章
阅读全文
1 0
原创粉丝点击