📜 ⬆️ ⬇️

Own presentation (view) in Spring MVC

If you worked with Spring MVC, you probably noticed that it supports many different views (view) that allow you to generate pdf, excel, csv using jstl, freemarker, velocity and jasperReports. But what if you needed to fill out any document in docx format with a template and transfer it to the user? On the one hand, you can use access to the standard response in the method, and if you plan to use this generation many times, then put it into a separate class. But this solution is not very elegant and violates the MVC pattern. To prevent this from happening, you can write your own view. To do this, you will need to inherit from one of the abstract views (view) for example from AbstractTemplateView. For docx, this view looks like this:
package ru.habrahabr.spring.view ;

import org.apache.commons.code.binary.Base64 ;
import org.docx4j.XmlUtils ;
import org.docx4j.openpackaging.io.SaveToZipFile ;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage ;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart ;
import org.docx4j.wml.Document ;
import org.springframework.web.servlet.view.AbstractTemplateView ;

import javax.servlet.ServletOutputStream ;
import javax.servlet.http.HttpServletRequest ;
import javax.servlet.http.HttpServletResponse ;
import java.net.URLEncoder ;
import java.util.HashMap ;
import java.util.Locale ;
import java.util.Map ;
')
public class DocxView extends AbstractTemplateView {

public DocxView ( ) {
setContentType ( "application / vnd.openxmlformats-officedocument.wordprocessingml.document" ) ;
}

@ Override
protected boolean generatesDownloadContent ( ) {
return true ;
}

@ Override
protected void renderMergedTemplateModel ( Map < String, Object > model,
HttpServletRequest request, HttpServletResponse response ) throws Exception {

WordprocessingMLPackage wordMLPackage =
WordprocessingMLPackage. load ( getApplicationContext ( ) . getResource ( getUrl ( ) ) . getFile ( ) ) ;

MainDocumentPart documentPart = wordMLPackage. getMainDocumentPart ( ) ;
org. docx4j . wml . Document wmlDocumentEl = ( org. Docx4j . Wml . Document ) documentPart. getJaxbElement ( ) ;
String xml = XmlUtils. marshaltoString ( wmlDocumentEl, true ) ;

HashMap < String, String > mappings = new HashMap < String, String > ( ) ;
for ( Object key: model. keySet ( ) ) {
mappings put ( key. toString ( ) , model. get ( key ) . toString ( ) ) ;
}

Object obj = XmlUtils. unmarshallFromTemplate ( xml, mappings ) ;
documentPart. setJaxbElement ( ( Document ) obj ) ;

String name = model. get ( "filename" ) + ".docx" ;

response. setHeader ( "Content-Disposition" , "attachment" + name ) ;

ServletOutputStream out = response. getOutputStream ( ) ;
( new SaveToZipFile ( wordMLPackage ) ) . save ( out ) ;

out. flush ( ) ;

}

public boolean checkResource ( Locale locale ) {
return getApplicationContext ( ) . getResource ( getUrl ( ) ) . exists ( ) ;
}

}

In the constructor, we set the content type, and in the generatesDownloadContent method, we indicate that the content type is downloadable. And in renderMergedTemplateModel , we read the file by getting the path to it from getUrl and fill in the document by template. Reading template data comes from model. checkResouce is required to check if the view file is present.

Now, when the view is ready, you need to configure a chain of view locators (ResolveViewers). To do this, add to mvc-config.xml:
< bean id = "docxViewResolver" class = "org.springframework.web.servlet.view.UrlBasedViewResolver" >
< property name = "viewClass" value = "ru.habrahabr.spring.view.DocxView" > </ property >
< property name = "order" value = "1" />
< property name = "prefix" value = "/ WEB-INF / views /" />
< property name = "suffix" value = ".docx" />
</ bean >

< bean id = "jspViewResolver" class = "org.springframework.web.servlet.view.InternalResourceViewResolver" >
< property name = "prefix" value = "/ WEB-INF / views /" />
< property name = "suffix" value = ".jsp" />
</ bean >


On setting up UrlBasedViewResolver worth staying in more detail. This locator allows you to use any View, including self-written ones, which is indicated via viewClass , then prefix is indicated, suffix is the same as for the standard view locator, order is required to specify the order of processing locators in the chain. As a result, the UrlBasedViewResolver locator will first be called, which will create a View and call the checkResource () method to check for the presence of the necessary file for the view. If it is not there, the method will return false, and the locator will return null instead of the view and the chain will go to the InternalResourceViewResolver .
Then you can put the views in the views test.docx file for example in the views / docx directory. And then in the usual way we create a controller method:
package ru.habrahabr.spring.controller ;

import org.springframework.stereotype.Controller ;
import org.springframework.ui.Model ;
import org.springframework.util.MultiValueMap ;
import org.springframework.web.bind.annotation.RequestMapping ;

Controller
public class DocxController {

@RequestMapping ( value = "/ get / docx" , method = RequestMethod. GET )
public String doGenerate ( Model model ) {
model. addAttribute ( "filename" , "test" ) ;
model. addAttribute ( "test" , "test" ) ;
return "docx / test" ;

Next, call the get / docx method and get the file in docx.

Source: https://habr.com/ru/post/101546/


All Articles