Я использую Spring Content Negotiation и OpenCSV для вывода CSV файла клиенту.
Итак, у меня есть этот контроллер:
@RequestMapping(value = { "", "/" }, method = RequestMethod.GET, produces = "text/csv")
@esponseStatus(HttpStatus.OK)
public Model getCustomersView(HttpServletRequest request, Model model)
throws InvalidBusinessContractDataException {
Customer[] customers = customerService.findCustomers();
return model.addAttribute("customers", customers);
}
И этот Media Type Translator:
@Component("viewNameTranslator")
public final class MediaTypeRequestToViewTranslator implements
RequestToViewNameTranslator {
@Autowired
private ContentNegotiationManager contentNegotiationManager;
private DefaultRequestToViewNameTranslator defaultTranslator = new DefaultRequestToViewNameTranslator();
// a list of media types to ignore - not output on the translated view
private List<String> ignoredTypes = Arrays.asList("text/html");
@Override
public String getViewName(HttpServletRequest request) {
// first resolve the media type (see below)
String mediaType = resolveMediaType(request);
// delegate to the default translator to get the view name
String viewName = defaultTranslator.getViewName(request);
// concatenate the resolved media type to the default name and return it
return viewName + mediaType;
}
private String resolveMediaType(HttpServletRequest request) {
try {
// use the content negotiation manager to resolve the media type
// from the request. The manager does it
// according to its own search path and preferences - using the
// suffix, using the Accept header and
// preferring one of them. The result would always be a single type
// or none at all, but it returns a list
List<MediaType> types = contentNegotiationManager
.resolveMediaTypes(new ServletWebRequest(request));
// resolve to a single type
String type = types == null || types.size() == 0 ? "" : types
.get(0).toString();
// if it not in the ignored media types - prepend a semi-colon and
// return it
return ignoredTypes.contains(type) ? "" : ";" + type;
} catch (HttpMediaTypeNotAcceptableException e) {
return "";
}
}
}
И эта точка зрения:
@Component("customers;text/csv")
public final class CustomerCsvView implements View {
@Override
public String getContentType() {
return "text/csv";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
response.setContentType("text/csv");
Customer[] customers = (Customer[]) model.get("customers");
CSVWriter writer = new CSVWriter(new OutputStreamWriter(response
.getOutputStream()));
writer.writeNext(new String[] {"id", "fName", "lName"});
for (Customer c : customers) {
System.out.println("FOO");
writer.writeNext(new String[] {
new Long(c.getCustomerId()).toString(), c.getFirstName(), c.getLastName() });
}
}
}
Когда я нажимаю на этот контроллер с браузером, дважды появляется "FOO" на моей консоли, открывается диалоговое окно сохранения файла с загружаемым тестовым /CSV файлом и составляет 0 байт.
Это как изменения ответа не сохраняются.
Что?
задайте тип содержимого и данные с флеша перед отправкой, затем закройте outputwriter
response.setContentType("text/csv;charset=utf-8")
response.setHeader("Content-Disposition","attachment; filename=\yourData.csv\"");
OutputStream resOs= response.getOutputStream();
OutputStream buffOs= new BufferedOutputStream(resOs);
OutputStreamWriter outputwriter = new OutputStreamWriter(buffOs);
CsvWriter writer = new CsvWriter(outputwriter);
// write data in csvwrite in loop
наконец, закрыть флеш и закрыть его.
outputwriter.flush();
outputwriter.close();
Будет лучше, если вы будете использовать @ResponseBody HttpMessageConverter с пружиной 3.0
В контроллере вам это нравится
@RequestMapping(value = "/getFullData2.html", method = RequestMethod.GET, consumes = "text/csv")
@ResponseBody // indicate to use a compatible HttpMessageConverter
public CsvResponse getFullData(HttpSession session) throws IOException {
List<CompositeRequirement> allRecords = compReqServ.getFullDataSet((String) session.getAttribute("currentProject"));
return new CsvResponse(allRecords, "yourData.csv");
}
Напишите HttpMessageConverter:
public class CsvMessageConverter extends AbstractHttpMessageConverter<CsvResponse> {
public static final MediaType MEDIA_TYPE = new MediaType("text", "csv", Charset.forName("utf-8"));
public CsvMessageConverter() {
super(MEDIA_TYPE);
}
protected boolean supports(Class<?> clazz) {
return CsvResponse.class.equals(clazz);
}
protected void writeInternal(CsvResponse response, HttpOutputMessage output) throws IOException, HttpMessageNotWritableException {
output.getHeaders().setContentType(MEDIA_TYPE);
output.getHeaders().set("Content-Disposition", "attachment; filename=\"" + response.getFilename() + "\"");
OutputStream out = output.getBody();
CsvWriter writer = new CsvWriter(new OutputStreamWriter(out), '\u0009');
List<CompositeRequirement> allRecords = response.getRecords();
for (int i = 1; i < allRecords.size(); i++) {
CompositeRequirement aReq = allRecords.get(i);
writer.write(aReq.toString());
}
writer.close();
}
}
Простой объект для связывания
public class CsvResponse {
private final String filename;
private final List<CompositeRequirement> records;
public CsvResponse(List<CompositeRequirement> records, String filename) {
this.records = records;
this.filename = filename;
}
public String getFilename() {
return filename;
}
public List<CompositeRequirement> getRecords() {
return records;
}
}