spring file download using ResponseBody - java

Hello i'm trying create an application allowing me host any kind of file.
In order to do it i'm exececuting following magic:
#RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
#ResponseBody
public FileSystemResource
getFile(
#PathVariable("file_name") String fileName) {
System.out.println(fileName);
String filePath = "./files/";
return new FileSystemResource(new File(filePath+fileName));
}
But this approach brings three unwanted problems:
Some random data is beeing appended to the file
The file gets opened in the browser window instead of beeing
downloaded - i've tried to hack this using something like
produces = "application/octet-stream"
but it only resulted in 406 error.
The test.txt is beeing truncated into test, i found a walkaround in providing the app with test.txt/ as fileName but it looks a bit messy.

As stated on spring manual
As with #RequestBody, Spring converts the returned object to a
response body by using an HttpMessageConverter
I think your problem is spring doesn't come with a HttpMessageConverter than can process FileSystemResource.
A list of builtin HttpMessageConverter is available here. I suggest you try converting your response into byte array somehow, maybe it will pick ByteArrayHttpMessageConverter instead and help solve your issue

I used code like this to return image
#RequestMapping(value = "/image/{id}", method = RequestMethod.GET)
public String getImage(... HttpServletResponse response) {
response.setContentType(image/png);
(response.getOutputStream()).write(imageByteArray);
}
I think you have to define proper mime type and send your data to response.

Related

Spring Boot, upload any amount of files in REST controller

I'd like to create an endpoint which accepts any amount od different files from user.
For example:
Then, I'd like to receive it in my controller as a Map<String, FilePart> (or any other structure from which I'll know which file is which):
{
"file1": "cactus-logo.png",
"file2": "logo.png",
"file3": "logo.png" (this one is actually different than file2 but has the same name)
}
I tried some combinations of #RequestPart...
When I do:
#RequestPart Map<String, FilePart> files
or
#RequestPart MultiValueMap<String, FilePart> files
I'm getting:
org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Required request part 'files' is not present"
When I do:
#RequestPart("files") List<FilePart> files
I need to submit files like that:
And then I don't have the information which file is which (if they have the same name):
Finally, I can do:
#RequestPart("file1") FilePart file1,
#RequestPart("file2") FilePart file2,
#RequestPart("file3") FilePart file3
And then it works as expected, but with that, it's possible to submit always only 3 files. I'd like to submit any number of files.
As suggested in comments:
#PutMapping(value = "/{component}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void upload(
#RequestParam Map<String, MultipartFile> files
) throws IOException {
and the map is always empty:
You should use #RequestParam
#PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void upload(#RequestParam Map<String, MultipartFile> body) {
}
Then post via form-data media ie:
file1: select file
file2: select file
file3: select file
....
I did not mention I'm using reactive webflux, thus solution posted by #Toàn Nguyễn Hải does not work in my case. I believe it works in non-reactive applications.
For web flux, the following works:
To be fair, I won't accept any answer, because both of them are fine.
Thanks!

can we get filename from byteArrayResource?

It's about uploading xlsx file. Initially im getting xlsx file as javax.servlet.http.Part and trying to convert as ByteArrayResource Since i need to pass via RestTemplate.
new ByteArrayResource(IOUtils.toByteArray(input.getFile().getInputStream())
Here input.getFile() is javax.servlet.http.Part
As im converting here into ByteArrayResource and sending back to another Layer for business logic, can i get filename from ByteArrayResource ?
Anyhow we can get filename from Part, I want to know is that possible to get filename from ByteArrayResource or ByteArray ?
This is late but if anyone is still looking for an answer.
ByteArrayResource doesn't have any filename by default. See the super class method AbstractResource.getFileName(), it returns null. So until you set the name explicitly while creating ByteArrayResource, it will remain null.
You can achieve this like
ByteArrayResource resource = new ByteArrayResource(bytearray) {
#Override
public String getFilename() {
return "somename";
};
}
Now how to use the actual file name instead of "somename". If you're using Spring, you can define a method in your controller accepting Multipart as an argument.
public ResponseEntity<byte[]> handleFileOperation(Multipart file) {}
Once you get the MultipartFile instance, you can use it the way you want.
You'll probably need to use FileNameAwareByteArrayResource as outlined in Spring-boot MultipartFile issue with ByteArrayResource.
You'll be able to get the filename from it then.

Sending byte[] to Java REST POST changes actual array

I have a Spring REST service set up which has a POST method to receive a byte array. The goal of this method is to accept a zipped JSON string, hence the byte array. The unzipped string is to be mapped to a list and then sent to a database.
#RequestMapping(value = "/receiveData.do", method = RequestMethod.POST)
public
#ResponseBody boolean receiveData(#RequestParam byte[] data,
#RequestParam String configurationName) {
String jsonString = ZipController.unZip(data);
JSONMapper jsonMapper = new JSONMapper();
List<Message> messages = null;
try {
messages = jsonMapper.parseToMessage(jsonString, configurationName);
saveData(messages);
} catch (Exception e) {
return false;
}
return true;
}
The zipping and the unzipping of the String works without flaws, as tests that I have written show. The converting to a list and then storing it in a database works perfectly too. However, when I zip a string, and send it to the address, somehow it isn't recognized as a zip anymore. Further digging revealed that the actual byte[] has completely changed in the process. Before I send it, the array prints to
[B#7186333e
However, after sending it, it suddenly prints to (for example)
[B#fee07cc
This appears to be random, but the [B# is always the same. The rest is random. I am using the REST-assured framework to test, but SOAPui gives the same problem. The code I'm using to send the call is:
expect().
body(containsString("true")).
when().
given().
parameters("data",ZipController.zip(correctJSONMessage)).
post("/receiveData.do");
The method returns a true or false as feedback whether it succeded or not.
The whole service runs on a Tomcat 6 server, is it possible that this messes with the array? How can I fix this?

Jersey Post request - How to perform a file upload with an unknown number of additional parameters?

I asked something like this previously, but upon re-reading my original post, it was not easy to understand what I was really asking. I have the following situation. We have (or at least I'm trying to get working) a custom file upload procedure that will take in the file, a set number of 'known' metadata values (and they will always be there), as well as potentially an unknown number of additional metadata values. The service that exists currently uses the Jersey framework (1.16)
I currently have both client and server code that handles dealing with the file upload portion and the known metadata values (server code below)
#POST
#Path("asset/{obfuscatedValue0}/")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public UUID uploadBlob(#PathParam("obfuscatedValue0") Integer obfuscatedValue0,
#FormDataParam("obfuscatedValue1") String obfuscatedValue1,
#FormDataParam("obfuscatedValue2") String obfuscatedValue2,
#FormDataParam("obfuscatedValue3") String obfuscatedValue3,
#FormDataParam("obfuscatedValue4") String obfuscatedValue4,
#FormDataParam("obfuscatedValue5") String obfuscatedValue5,
#FormDataParam("file") InputStream uploadedInputStream) {
.....
}
...and excerpt of client code:
Builder requestBuilder = _storageService
.path("asset")
.path(obfuscatedValue0.toString())
.type(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.APPLICATION_JSON);
FormDataMultiPart part = new FormDataMultiPart()
.field("file", is, MediaType.TEXT_PLAIN_TYPE) // 'is' is an inputstream from earlier in code.
.field("obfuscatedValue1", obfuscatedValue1)
.field("obfuscatedValue2", obfuscatedValue2)
.field("obfuscatedValue3", obfuscatedValue3)
.field("obfuscatedValue4", obfuscatedValue4)
.field("obfuscatedValue5", obfuscatedValue5);
storedAsset = requestBuilder.post(UUID.class, part);
However, I need to pass a map of additional parameters that will have an unknown number of values/names. From what I've seen, there is no easy way to do this using the FormDataParam annotation like my previous example.
Based upon various internet searches related to Jersey file uploads, I've attempted to convert it to use MultivaluedMap with the content type set to "application/x-www-form-urlencoded" so it resembles this:
#POST
#Path("asset/{value}/")
#Consumes("application/x-www-form-urlencoded")
public UUID uploadBlob(#PathParam(value), MultivaluedMap<String,String> formParams) {
....
}
It's my understanding that MultivaluedMap is intended to obtain a general map of form parameters (and as such, cannot play nicely together in the same method bearing #FormDataParam annotations.) If I can pass all this information from the Client inside some sort of map, I think I can figure out how to handle parsing the map to grab and 'doMagic()' on the data to get what I want done; I don't think I'll have a problem there.
What I AM fairly confused about is how to format the request client-side code when using this second method within the jersey framework. Can anyone provide some guidance for the situation, or some suggestions on how to proceed? I'm considering trying the solution proposed here and developing a custom xml adapter to deal with this situation, and sending xml instead of multipart-form-data but I'm still confused how this would interact with the InputStream value that will need to be passed. It appears the examples with MultivaluedMap that I've seen only deal with String data.

non-static resources in Spring

I got Spring 2.5.6 and I'm trying to implement some stupid functionality within.
I have a photo directory somewhere in my server machine and I want to expose these photos to the user. What is the common solution for this issue?
P.S.
By now I'm writing my photos to the response using my own controller, and that is not very handy for me, cause I want to keep my photo's url nice and friendly (for example like /myServlet/images/012345.jpg).
Having your own controller is a fine solution. Here is a simplified copy from my PictureController:
#RequestMapping("/pictures/{filename}.{extension}")
public void getPicture(#PathVariable String filename,
#PathVariable String extension, OutputStream outputStream,
HttpServletResponse response) {
DateTime cachePeriod = new DateTime();
cachePeriod = cachePeriod.plusDays(5);
response.setDateHeader("Expires", cachePeriod.getMillis());
pictureService.writePicture(filename + "." + extension, outputStream);
}
Where pictureService simply does IOUtils.copy(imageStream, outputStream)

Categories