How to go from spring mvc multipartfile into zipinputstream - spring-mvc

I have a Spring MVC controller that accepts a MultipartFile, which will be a zip file. The problem is I can't seem to go from that to a ZipInputStream or ZipFile, so that I can go through the entries. It either closes the stream prematurely, produces an empty file, or as in the case below, zipInputStream.getNextEntry() returning null.
This is my MVC controller:
#RequestMapping(value = "/content/general-import", method = RequestMethod.POST)
public ModelAndView handleGeneralUpload(
#RequestParam("file") MultipartFile file) throws IOException {
// hard code the signature for the moment
String signature = "RETAILER_GROUP:*|CHANNEL:*|LOCALE:de-AT|INDUSTRY:5499";
LOG.info("Processing file archive: {} with signature: {}.", file.getName(), signature);
ModelAndView mav = new ModelAndView();
mav.setViewName("contentUpload");
LOG.debug("File={} is empty={}.", file.getName(), file.isEmpty());
if (!file.isEmpty()) {
processFileZipEntry(file, signature);
mav.addObject("form", UploadViewModel.make("/content/general-import", "Updated content with file"));
return mav;
} else {
mav.addObject("form", UploadViewModel.make("/content/general-import", "Could not update content with file"));
return mav;
}
}
It delegates to the following method for processing:
protected void processFileZipEntry(MultipartFile file, String signature) throws IOException {
byte[] bytes = file.getBytes();
LOG.debug("Processing archive with bytes={}.", file.getBytes().length);
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(bytes));
LOG.debug("Processing archive with size={}.", file.getSize());
ZipEntry entry = null;
while ((entry = zis.getNextEntry()) != null) {
LOG.debug("Processing file={} is directory?={}.", entry.getName(), entry.isDirectory());
// process each file, based on what it is and whether its a directory etc.
if (!entry.isDirectory()) {
// if the entry is a file, extract it
LOG.debug("Processing entry: {}",entry.getName());
int length = (int) entry.getSize();
Content contentToSave = null;
if(entry.getName().contains("gif")) {
contentToSave = Content.makeImage(entry.getName(), Content.GIF, signature, getBytesFrom(zis, "gif"));
} else if (entry.getName().contains("png")) {
contentToSave = Content.makeImage(entry.getName(), Content.PNG, signature, getBytesFrom(zis, "png"));
} else if (entry.getName().contains("jpeg")) {
contentToSave = Content.makeImage(entry.getName(), Content.JPEG, signature, getBytesFrom(zis, "jpeg"));
} else if (entry.getName().contains("json")) {
contentToSave = Content.makeFile(entry.getName(), Content.JSON, signature, getStringFrom(zis, length));
} else if (entry.getName().contains("js")) {
contentToSave = Content.makeFile(entry.getName(), Content.JS, signature, getStringFrom(zis, length));
} else if (entry.getName().contains("css")) {
contentToSave = Content.makeFile(entry.getName(), Content.CSS, signature, getStringFrom(zis, length));
}
Content contentAleadyThere = contentService.fetch(entry.getName());
if(contentAleadyThere != null) {
LOG.warn("Replacing file: {} with uploaded version.", contentToSave.getName());
}
contentService.put(contentToSave);
LOG.info("Persisted file: {} from uploaded version.", contentToSave.getName());
}
}
}
Basically, in this permutation, the file bytes are there, but there are no entries (zis.getNextEntry() does not exist. I can see that the zip file contains files, and the byte[] has about 3MB worth of stuff, so something must be going wrong with the streaming. Does anyone have a recipe for going from MultipartFile to ZipFile or ZipInputStream?
EDIT
To give you more information, I have a test harnass around this code, by using a MockMvc
#Test
public void testProcessingGeneralUpload() throws Exception {
Resource template = wac.getResource("classpath:lc_content/content.zip");
System.out.println("template content length: " + template.contentLength());
System.out.println("template path: " + template.getFile().getPath());
System.out.println("template filename: " + template.getFilename());
MockMultipartFile firstFile = new MockMultipartFile(
"file", "content.zip", MediaType.APPLICATION_OCTET_STREAM_VALUE, extractFile(template.getFile()));
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.fileUpload("/content/general-import")
.file(firstFile))
.andExpect(status().isOk())
.andExpect(view().name("contentUpload"))
.andExpect(model().attributeExists("form")).andReturn();
// processing assertions
ModelMap modelMap = mvcResult.getModelAndView().getModelMap();
Object object = modelMap.get("form");
assertThat(object, is(not(nullValue())));
assertThat(object, is(instanceOf(UploadViewModel.class)));
UploadViewModel addModel = (UploadViewModel) object;
assertThat(addModel.getMessage(), is(notNullValue()));
assertThat(addModel.getPostUrl(), is(notNullValue()));
assertThat(addModel.getPostUrl(), is("/content/general-import"));
assertThat(addModel.getMessage(), is("Updated content with file"));
// persistence assertions
assertThat(contentDao.findByName("/content/control/basket-manager.js"), is(notNullValue()) );
}
The extractFile method is as follows:
private byte[] extractFile(File zipFile) throws IOException {
ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFile));
System.out.println("length of file: " + zipFile.length());
byte[] output = null;
try {
byte[] data = new byte[(int)zipFile.length()];
zipIn.read(data);
zipIn.close();
output = data;
} catch (IOException e) {
e.printStackTrace();
}
return output;
}
The length of bytes it produces is 3617817, which is the size I expect, and this is fed into the controller method at the top of this question.
I have continued working the problem. The size of the file is correct, it is a zipped file (it unpacks via the OS perfectly), and yet no ZipEntry enumeration.

I would for starters rewrite some of the code instead of doing things in memory with additional byte[].
You are using Spring's Resource class so why not simply use the getInputStream() method to construct the MockMultipartFile as you want to upload that file.
Resource template = wac.getResource("classpath:lc_content/content.zip");
MockMultipartFile firstFile = new MockMultipartFile(
"file", "content.zip", MediaType.APPLICATION_OCTET_STREAM_VALUE, template.getInputStream());
The same for your upload processing code the ZipInputStream can also be constructed on another InputStream which is also provided by the MultipartFile interface.
protected void processFileZipEntry(MultipartFile file, String signature) throws IOException {
LOG.debug("Processing archive with size={}.", file.getSize());
ZipInputStream zis = new ZipInputStream(file.getInputStream());
Wouldn't be the first time that jugling around with byte[] gives a problem. I also vaguely recall some issues with ZipInputStream which lead us to use ZipFile but for this you will first have to store the file in a temp directoy using the transferTo method on MultipartFile.
File tempFile = File.createTempFile("upload", null);
file.transferTo(tempFile);
ZipFile zipFile = new ZipFle(tempFile);
// Proces Zip
tempFile.delete();

Related

Spring Boot MVC -> Excel data corrupted on download

I am using ModelAndView pattern to return excel representation of data that is generated in the Controller using Apache POI library.
However the excel gets corrupted(special characters are replaced with ?) when it gets downloaded. If I write the excel to file before pushing it out on the HTTP response, then a valid excel is output.
Here is the controller code that pushes control to ModelAndView
Map<String, Object> model = new HashMap<String, Object>();
model.put(ExcelBusinessReportView.KEY_REPORT_DISPLAY_DATA, reportData);
model.put(ExcelBusinessReportView.KEY_REPORT_DATE, reportRequestDTO.getReportDateUTCAtMidnight());
return new ModelAndView("excelBusinessReportView", model);
And here is the view class
#Service(value = "excelBusinessReportView")
public class ExcelBusinessReportView extends AbstractXlsView {
public static final String KEY_REPORT_DISPLAY_DATA = "reportData";
public static final String KEY_REPORT_DATE = "reportDate";
private static final String MIME_TYPE_EXCEL = "application/ms-excel";
private static final String HEADER_VALUE_CONTENT_DISPOSITION = "attachment; filename=qup_report.xls";
private static final String[] SUMMARY_HEADERS = ........
private static final String[] DETAIL_HEADERS = ........
#Override
protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
BusinessSlotReportResource reportDisplayData = (BusinessSlotReportResource) model.get(KEY_REPORT_DISPLAY_DATA);
DateTime reportDate = (DateTime) model.get(KEY_REPORT_DATE);
// Build excel document
Sheet sheet = workbook.createSheet(reportDate.toString(CommonConstants.IST_DATE_FORMATTER_PATTERN));
sheet.setDefaultColumnWidth((short) 12);
Integer currentRow = 0;
// Build summary data
currentRow = this.buildSummaryData(workbook, sheet, reportDisplayData, currentRow);
// Create margin rows
sheet.createRow(currentRow++);
sheet.createRow(currentRow++);
// Build detail data
this.buildDetailsData(workbook, sheet, reportDisplayData, currentRow);
response.setContentType(MIME_TYPE_EXCEL);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, HEADER_VALUE_CONTENT_DISPOSITION);
}
Content of excel when written to file in the view
–œ‡°±·;˛ˇ ˛ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇRoot Entryˇˇˇˇˇˇˇˇ#Workbookˇˇˇˇˇˇˇˇˇˇˇˇ˛ˇˇˇ˝ˇˇˇ˛ˇˇˇ ˛ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ
 !"#$%&'()*+,-./0˛ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ ”ÃA·∞¡‚\panilallewar
Same part of the excel when downloaded
��ࡱ�;�� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Root Entry��������#Workbook������������������������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
 !"#$%&'()*+,-./0�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��A����\panilallewar
Usually instead of putting the file in the model I write it directly in the response.
The following code is for xlsx format, but the concept is the same for previous versions of excel.
This endpoint is accepting a JSON which will be mapped to MyPojo.
#RequestMapping(value = "exportToExcel", method = RequestMethod.POST)
public #ResponseBody HttpEntity<byte[]> generateExcel(#Valid #RequestBody final MyPojo data) throws IOException {
final File file = File.createTempFile("MyExcelReport", "xlsx");
file.deleteOnExit();
final Path path = file.toPath();
try (final FileOutputStream fileOut = new FileOutputStream(file)) {
try (final XSSFWorkbook workbook = new XSSFWorkbook()) {
final XSSFSheet sheet = workbook.createSheet(SHEET_NAME);
//fill your excel sheets
workbook.write(fileOut);
final byte[] byteArray = Files.readAllBytes(path);
final HttpHeaders header = new HttpHeaders();
header.setContentType(new MediaType("application", "vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
header.set("Content-Disposition", "inline; filename=MyExcelReport.xlsx");
header.setContentLength(byteArray.length);
return new HttpEntity<>(byteArray, header);
} catch (final Exception e) {
LOG.error("Error during creation of excel report", e);
throw e;
} finally {
if (path != null) {
try {
Files.delete(path);
} catch (final IOException e) {
LOG.error("Unable to delete file:" + path.toString(), e);
}
}
}
}
}
Also if you are using a frontend framework like angular you have to properly setup the response type (https://stackoverflow.com/a/52703842/3657208)

Spring MVC Rest unable to return JPEG with "could not find acceptable representation error"

I have a spring-boot app acting as a image server. I POST an image to be persisted to mongodb. I then retrieve it, resize and return it.
Here is the project configuration:
#Configuration
public class AllResources extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer matcher) {
matcher.setUseRegisteredSuffixPatternMatch(true);
}
}
And here is the endpoint:
#RequestMapping(value = "images/{filename}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<BufferedImage> getSizedImage(#PathVariable String filename, #RequestParam int width, #RequestParam int height) throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
GridFSDBFile savedFile = mongoFileService.getStore( filename );
if ( savedFile != null ) {
try {
BufferedImage image = ImageIO.read( savedFile.getInputStream() );
image = resize( image, Method.SPEED, width, height, Scalr.OP_ANTIALIAS );
LOGGER.info("Returning Filename " + savedFile.getFilename() + " sized to " + width + " X " + height);
return new ResponseEntity<BufferedImage>(image, headers, HttpStatus.OK);
} catch ( Exception ex ) {
ex.printStackTrace();
LOGGER.error( "Error sizing file " + filename + ": " + ex.getMessage() );
return new ResponseEntity<BufferedImage>(null, headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
} else {
LOGGER.error( "Could not find requested file " + filename );
return new ResponseEntity<BufferedImage>(null, headers, HttpStatus.NOT_FOUND);
}
}
The image is retrieved and resized (I can actually preview when debugging in IntelliJ). But when it is returned, I get the following error:
Controller [org.springframework.boot.autoconfigure.web.BasicErrorController]
Method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:195)
And I see this in the logs:
Method [error] returned [<406 Not Acceptable,{timestamp=Sat Aug 22 11:05:59 MDT 2015, status=406, error=Not Acceptable, exception=org.springframework.web.HttpMediaTypeNotAcceptableException, message=Could not find acceptable representation, path=/images/1440263145562_profile_04132015.PNG},{}>]
2015-08-22 11:05:59.711 DEBUG 2478 --- [0.1-3000-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Written [{timestamp=Sat Aug 22 11:05:59 MDT 2015, status=406, error=Not Acceptable, exception=org.springframework.web.HttpMediaTypeNotAcceptableException, message=Could not find acceptable representation, path=/images/1440263145562_profile_04132015.PNG}] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#5bf1ba3a]
2015-08-22 11:05:59.711 DEBUG 2478 --- [0.1-3000-exec-3] o.s.web.servlet.DispatcherServlet : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
I have tried with & without the #ResponseBody and it doesn't appear to make a difference either. I have the produces and content type set correctly (I think).
I added these converters (although I thought SpringBoot provided these), but to no avail:
#Configuration
public class AllResources extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer matcher) {
matcher.setUseRegisteredSuffixPatternMatch(true);
}
#Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter(){
ByteArrayHttpMessageConverter bam = new ByteArrayHttpMessageConverter();
List<org.springframework.http.MediaType> mediaTypes = new LinkedList<MediaType>();
mediaTypes.add(org.springframework.http.MediaType.APPLICATION_JSON);
mediaTypes.add(org.springframework.http.MediaType.IMAGE_JPEG);
mediaTypes.add(org.springframework.http.MediaType.IMAGE_PNG);
mediaTypes.add(org.springframework.http.MediaType.IMAGE_GIF);
mediaTypes.add(org.springframework.http.MediaType.TEXT_PLAIN);
bam.setSupportedMediaTypes(mediaTypes);
return bam;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter mapper = new MappingJackson2HttpMessageConverter();
converters.add(mapper);
converters.add(byteArrayHttpMessageConverter());
super.configureMessageConverters(converters);
}
}
I hope someone can see what is causing this issue.
Try this piece of code
#RequestMapping("/sparklr/photos/{id}")
public ResponseEntity<BufferedImage> photo(#PathVariable String id) throws Exception {
InputStream photo = sparklrService.loadSparklrPhoto(id);
if (photo == null) {
throw new UnavailableException("The requested photo does not exist");
}
BufferedImage body;
MediaType contentType = MediaType.IMAGE_JPEG;
Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByMIMEType(contentType.toString());
if (imageReaders.hasNext()) {
ImageReader imageReader = imageReaders.next();
ImageReadParam irp = imageReader.getDefaultReadParam();
imageReader.setInput(new MemoryCacheImageInputStream(photo), true);
body = imageReader.read(0, irp);
} else {
throw new HttpMessageNotReadableException("Could not find javax.imageio.ImageReader for Content-Type ["
+ contentType + "]");
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
return new ResponseEntity<BufferedImage>(body, headers, HttpStatus.OK);
}
EDIT:
We have to configure MessageConverter
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new BufferedImageHttpMessageConverter());
}
Here I have Content Negotiator like this
#Bean
public ContentNegotiatingViewResolver contentViewResolver() throws Exception {
ContentNegotiatingViewResolver contentViewResolver = new ContentNegotiatingViewResolver();
ContentNegotiationManagerFactoryBean contentNegotiationManager = new ContentNegotiationManagerFactoryBean();
contentNegotiationManager.addMediaType("json", MediaType.APPLICATION_JSON);
contentViewResolver.setContentNegotiationManager(contentNegotiationManager.getObject());
contentViewResolver.setDefaultViews(Arrays.<View> asList(new MappingJackson2JsonView()));
return contentViewResolver;
}
I think the message inside your logs can clear your doubts. Have a close look at your logs and comment me again
I think the problem is that none of the registered message converters knows how to write a BufferedImage to the response in the format dictated by the accept header. Try registering your own message converter that knows how to write out a BufferedImage in the requested format.

If I have a spring mvc rest controller returning byte[], how would I download to my android app using volley?

I need to implement a list view containing a thumbnail, and this thumbnail is loaded using volley networkimageview. How would I implement this if my controller looks like this:
#RequestMapping (value="/rest/getphoto/", produces=MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte [] get Image (#RequestParam ("imageId"));
I've found many examples regarding the usage of volley but they are not helping me. Besides, I am using secure connection. Thanks in advance.
EDIT: I'm including controller code in my spring mvc project and the portion of code in my android client requesting an image.
* Spring MVC *
#RequestMapping(value = "/rest/singlephoto/", method = RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] base64ImageForAndroid(#RequestParam("photoId") String photoIdParam, HttpServletRequest request)
{
String pathToLoad = "/path/default.png";
//HashMap<String, String> retVal = new HashMap<String, String>();
byte[] retVal;
try
{
long photoId = Long.parseLong(photoIdParam);
Photo photo = photoManager.getSinglePhoto(photoId);
if (photo != null)
pathToLoad = photo.getPath();
}
catch (NumberFormatException ex)
{
}
finally
{
try
{
File file = new File(pathToLoad);
retVal = FileUtils.readFileToByteArray(file);
}
catch (IOException ex)
{
retVal = null;
}
}
return retVal;
* Android Client Requesting with volley *
Bitmap thumb = imageCache.get(item.getThumbnailUrl() + "thumb");
if (thumb == null)
{
HttpHeaders headers = new HttpHeaders();
HttpBasicAuthentication auth = new HttpBasicAuthentication(this.username, this.password);
headers.setAuthorization(auth);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_OCTET_STREAM));
Listener<byte[]> imageLoadedListener = new Response.Listener<byte[]>() {
#Override
public void onResponse(byte[] photoByteArray) {
Bitmap bitmap = EfficientImageLoading.decodeBitmapFromByteArray(photoByteArray, viewHolder.thumbnail.getWidth(), viewHolder.thumbnail.getHeight());
viewHolder.thumbnail.setImageBitmap(bitmap);
imageCache.put(item.getThumbnailUrl() + "thumb", bitmap);
//Cache full size and recycle
Bitmap fullBmp = EfficientImageLoading.decodeImageFromByteFullSize(photoByteArray);
imageCache.put(item.getThumbnailUrl(), fullBmp);
}
};
ErrorListener errorListener = new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
viewHolder.thumbnail.setImageResource(R.drawable.ic_launcher);
}
};
this.singleInstance.addToRequestQueue(new CustomImageRequest(Request.Method.GET, item.getThumbnailUrl(), errorListener, headers, imageLoadedListener));
}
Application server logs show:
GET /app//photo/rest/singlephoto/?photoId=7 HTTP/1.1" 406 1067
That is 406-- forbidden or something like that. Also android's LogCat shows an error like the following: BasicNetwork.PerformRequest: Unexpected response code 406 for https://domain/app/singlephoto?photoId=7
Is there something wrong with my controller or my client or both?

How to import an xquery module using Saxon

I am having some troubles running an Xquery with Saxon9HE, which has a reference to an external module.
I would like Saxon to resolve the module with a relative path rather absolute.
the module declaration
module namespace common = "http://my-xquery-utils";
from the main xquery
import module namespace common = "http://my-xquery-utils" at "/home/myself/common.xquery";
from my java code
public class SaxonInvocator {
private static Processor proc = null;
private static XQueryEvaluator xqe = null;
private static DocumentBuilder db = null;
private static StaticQueryContext ctx = null;
/**
* Utility for debug, should not be called outside your IDE
*
* #param args xml, xqFile, xqParameter
*/
public static void main(String[] args) {
XmlObject instance = null;
try {
instance = XmlObject.Factory.parse(new File(args[0]));
} catch (XmlException ex) {
Logger.getLogger(SaxonInvocator.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex){
Logger.getLogger(SaxonInvocator.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.print(transform(instance, args[1], args[2]));
}
public static String transform(XmlObject input, String xqFile, String xqParameter) {
String result = null;
try {
proc = new Processor(false);
proc.getUnderlyingConfiguration().getOptimizer().setOptimizationLevel(0);
ctx = proc.getUnderlyingConfiguration().newStaticQueryContext();
ctx.setModuleURIResolver(new ModuleURIResolver() {
#Override
public StreamSource[] resolve(String moduleURI, String baseURI, String[] locations) throws XPathException {
StreamSource[] modules = new StreamSource[locations.length];
for (int i = 0; i < locations.length; i++) {
modules[i] = new StreamSource(getResourceAsStream(locations[i]));
}
return modules;
}
});
db = proc.newDocumentBuilder();
XQueryCompiler comp = proc.newXQueryCompiler();
XQueryExecutable exp = comp.compile(getResourceAsStream(xqFile));
xqe = exp.load();
ByteArrayInputStream bais = new ByteArrayInputStream(input.xmlText().getBytes("UTF-8"));
StreamSource ss = new StreamSource(bais);
XdmNode node = db.build(ss);
xqe.setExternalVariable(
new QName(xqParameter), node);
result = xqe.evaluate().toString();
} catch (SaxonApiException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static InputStream getResourceAsStream(String resource) {
InputStream stream = SaxonInvocator.class.getResourceAsStream("/" + resource);
if (stream == null) {
stream = SaxonInvocator.class.getResourceAsStream(resource);
}
if (stream == null) {
stream = SaxonInvocator.class.getResourceAsStream("my/project/" + resource);
}
if (stream == null) {
stream = SaxonInvocator.class.getResourceAsStream("/my/project/" + resource);
}
return stream;
}
}
If a change it into a relative path like
import module namespace common = "http://my-xquery-utils" at "common.xquery";
I get
Error on line 22 column 1
XQST0059: java.io.FileNotFoundException
I am not sure how the ModuleURIResolver should be used.
Saxon questions are best asked on the Saxon forum at http://saxonica.plan.io - questions asked here will probably be noticed eventually but sometimes, like this time, they aren't our first priority.
The basic answer is that for the relative URI to resolve, the base URI needs to be known, which means that you need to ensure that the baseURI property in the XQueryCompiler is set. This happens automatically if you compile the query from a File, but not if you compile it from an InputStream.
If you don't know a suitable base URI to set, the alternative is to write a ModuleURIResolver, which could for example fetch the module by making another call on getResourceAsStream().

Having more than one Get in Restful webservice

I am working on a project to connect test instruments and get request and response from it using webservice.
I have to request more than one service from the instrument, but when I use more than two #Gets in the server, I get an error in my browser saying
Cannot access WADL, please restart your restful webservice
This is my code,
GET
#Produces("text/html")
public String getHtml(){
String ins_name=null;
try {
String [] env=null;
//setting the environment variable.
String[]callAndArgs= {"python","instrument_name.py"};//Python and file name
Process p = Runtime.getRuntime().exec(callAndArgs,env,
new java.io.File("C:\\fakepath\\NetBeansProjects\\DemoApp1\\build\\web"));//executing Python file
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));//getting the value from Python file
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));// reading the error
ins_name = stdInput.readLine();//reading the output from the Pythonfile
System.out.println(ins_name);
}
catch (IOException e) {//catching the exception
System.out.println("exception occured");
e.printStackTrace();
System.exit(-1);
}
return ins_name;//returning the instrument name
}
#GET
#Produces("text/html")
public String getHtml1() {
String check=null;
String c1="hjhj";
String [] env=null;
//setting the environment variable.
try{
String[] callAndArgs= {"python","check_connection.py",c1};//Python and file name
Process p = Runtime.getRuntime().exec(callAndArgs,env,
new java.io.File("C:\\Users\\Balkishore\\Documents\\NetBeansProjects\\DemoApp1\\build\\web"));//executing Python file
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));//getting the value from Python file
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));// reading the error
check= stdInput.readLine();//reading the output from the Python file
System.out.println();
}
catch (IOException e) {//catching the exception
System.out.println("exception occured");
e.printStackTrace();
System.exit(-1);
}
return check;
}
/**
* Web service operation
}
/**
* PUT method for updating or creating an instance of GenericResource
* #param content representation for the resource
* #return an HTTP response with content of the updated or created resource.
*/
#PUT
#Consumes("text/html")
public String putHtml(String interface_name) {
try {
String [] env=null;
String [] callAndArgs= {"python","connection.py",this.interface_name=interface_name};//Python file with arguments
Process p = Runtime.getRuntime().exec(callAndArgs,env,
new java.io.File("C:\\fakepath\\NetBeansProjects\\DemoApp1\\build\\web"));//executing the Python file
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));//getting the input
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));//getting the error
interface_name = stdInput.readLine();//reading the output
System.out.println(interface_name);
}
catch (IOException e) {//catching the exception
System.out.println("exception occured");
e.printStackTrace();
System.exit(-1);
}
return interface_name;
}
}
I have also attached the image of the error message.
You can't define distinct "GET" for a resource unless you specify distinct Path for your methods #Path("/mypath") in addition to your resource path
#Path("/myRes")
public class myResource{
#GET #Path("/myAttr")
public void getAttr(...)
}

Resources