Sending a Zip file to Rest WebService - java

I'm trying to implement a method in my java based application that involves uploading a zip file to my server. Client is java, server is java (REST , jboss 7) . In the past I successfully managed to upload image files, but now, with a zip file i am having issues and my main doubt is if these issues are client related or server related (or both) .
So , my client looks like this
final HttpHeaders headers = HttpClientUtils.headersJSONAndAcceptJSON();
MultiValueMap<String, Object> requestMap = new LinkedMultiValueMap<String, Object>();
addMap("filename", filename, requestMap);
addMap("contenttype", contentType, requestMap);
addMap("type", type, requestMap);
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
int b = -1;
//file data is the inputstream created from the File
while ( (b = filedata.read())>= 0 ) {
bout.write(b);
}
ByteArrayResource rs = new ByteArrayResource( bout.toByteArray() ){
#Override
public String getFilename() {
return "";
}
};
addMap("resource", rs, requestMap);
} catch (IOException e1) {
throw new IllegalStateException("Error");
}
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.setAccept(Arrays.asList(HttpClientUtils.mtypeJSONUtf8()));
final String url = this.baseURL + summaryURL;
try {
ResponseEntity<Summary> rEntity = restTemplate.exchange(
url,
HttpMethod.POST,
HttpClientUtils.entity(headers, requestMap),
Summary.class
(...)
and meanwhile on the server side I have
#POST
#Path("/")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("application/json; charset=UTF-8")
public Summary addImportedSummary(#MultipartForm FileUploadFormObj imp)
{
Summary importedSummary = new Summary();
Map<String , String> newpath = new HashMap<String, String>();
if(imp.getFileData() != null)
{
ZipInputStream zip = new ZipInputStream(imp.getFileData());
ZipEntry entry;
try {
while ((entry = zip.getNextEntry()) != null)
{
if(entry.getName().endsWith(".html") || entry.getName().endsWith(".htm"))
{
if(entry.getSize() > 0)
{
StringWriter writer = new StringWriter();
IOUtils.copy(zip, writer, "UTF-8");
String content = writer.toString();
//do something with the content
}
}
}
zip.close();
} catch (IOException e) {
throw new BadRequestException("Error " + e);
}
}
The problem happens when I try to copy the file content with IOUtils or any other reader. I always get the exception
ZipException too many length or distance symbols
Now, I think the problem might be in the way I am sending the data due to the file being a zip but I don't know exactly where the problem is. Did everyone ever ran into a similar problem?

Related

How get instance of HttpServletResponse for download files with JAVA EE

I have a file txt on the server (previously generated). When user clicks on button it generates the file, now I want (additionally) download the file inside my function. But I can't make it work(I'm new on JAVA EE), cause I don't know how to get HttpServletResponse.
From web I call function with this:
#Path("getreport")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public JSONObject getreport(CommonInput input) {
JSONObject j = objectmapper.conertValue(reportBean.getreport(),JSONObject.class);
return j;
}
reprotBean has function:
public void getreport() {
//...doing many things
//generating my file
List<String> lines = new ArrayList<>();
lines.add("star file");
//..adding many lines
Path file = Paths.get("C:\\Users\\myuser\\file.txt");
Files.write(file, lines, StandardCharsets.UTF_8);
downloadFile();
//...doing many things
}
I found this way to download my file:
public void downloadFile(HttpServletResponse response){
String sourceFile = ""C:\\Users\\myuser\\file.txt"";
try {
FileInputStream inputStream = new FileInputStream(sourceFile);
String disposition = "attachment; fileName=outputfile.txt";
response.setContentType("text/txt");
response.setHeader("Content-Disposition", disposition);
response.setHeader("content-Length", String.valueOf(stream(inputStream, response.getOutputStream())));
} catch (IOException e) {
logger.error("Error occurred while downloading file {}",e);
}
}
private long stream(InputStream input, OutputStream output) throws IOException {
try (ReadableByteChannel inputChannel = Channels.newChannel(input); WritableByteChannel outputChannel = Channels.newChannel(output)) {
ByteBuffer buffer = ByteBuffer.allocate(10240);
long size = 0;
while (inputChannel.read(buffer) != -1) {
buffer.flip();
size += outputChannel.write(buffer);
buffer.clear();
}
return size;
}
}
When I try to use downloadFile(), it requires HttpServletResponse, and I don't have that parameter. I can't understand how to get that (how it works), or do I have to use another method for download my file?
All solutions I found requires HttpServletResponse (download files from browsers)
If you have that file generated already. Just need write it to HttpServletResponse
resp.setContentType("text/plain");
resp.setHeader("Content-disposition", "attachment; filename=sample.txt");
try(InputStream in = req.getServletContext().getResourceAsStream("sample.txt");
OutputStream out = resp.getOutputStream()) {
byte[] buffer = new byte[ARBITARY_SIZE];
int numBytesRead;
while ((numBytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, numBytesRead);
}
}
Be sure to make your file to be accessed by ServeletContext
If you are using Spring Rest framework. Can refer to below
#GetMapping("/download")
public ResponseEntity<byte[]> downloadErrorData() throws Exception {
List<Employee> employees = employeeService.getEmployees();
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(employees);
byte[] isr = json.getBytes();
String fileName = "employees.json";
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentLength(isr.length);
respHeaders.setContentType(new MediaType("text", "json"));
respHeaders.setCacheControl("must-revalidate, post-check=0, pre-check=0");
respHeaders.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName);
return new ResponseEntity<byte[]>(isr, respHeaders, HttpStatus.OK);
}
credit to: https://www.jeejava.com/file-download-example-using-spring-rest-controller/

Delete temporary files

I have developed a rest client using spring mvc which upload files and form data to a rest service using Jersey.
I can able to see the files that i uploaded, in rest client's Tomcat home directory.
How can i automatically delete the file that is stored in my tomcat, after i get a success response.
Some of my configuration for your reference,
Multipart config in "web.xml"
<multipart-config>
<location>/tmp</location>
<max-file-size>26214400</max-file-size>
<max-request-size>31457280</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
multipart config in "dispatcher-servlet.xml"
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
My business logic,
public Map<Object, Object> upload(ModelMap model) {
Map<Object, Object> responseMap = new HashMap<>();
sendMailBean = (SendMailBean) model.get("sendMailBean");
FormDataMultiPart formDataMultiPart = new FormDataMultiPart();
formDataMultiPart.field("firstName", sendMailBean.getFirstname());
formDataMultiPart.field("lastName", sendMailBean.getLastname());
formDataMultiPart.field("fromAddress", sendMailBean.getEmail());
formDataMultiPart.field("subject", sendMailBean.getSubject());
formDataMultiPart.field("text", sendMailBean.getMessage());
List<MultipartFile> files = sendMailBean.getAttachments();
try {
for(MultipartFile file : files) {
File convFile = convert(file);
FileDataBodyPart filePart = new FileDataBodyPart("files", convFile);
filePart.setContentDisposition(FormDataContentDisposition.name("files").fileName(file.getOriginalFilename()).build());
formDataMultiPart.bodyPart(filePart);
}
Client client = new Client();
WebResource webResource = client.resource(----rest url-----);
ClientResponse response = webResource.type(MediaType.MULTIPART_FORM_DATA).post(ClientResponse.class, formDataMultiPart);
if (response.getStatus() != 200) {
model.addAttribute("errormsg", "Failed : HTTP error code : " + response.getStatus());
responseMap.put("model", model);
responseMap.put("redirectToPage", "redirect:/views/error");
} else {
// responseMap.put("redirectToPage", "/views/email");
responseMap.put("model", model);
responseMap.put("redirectToPage", "");
}
}catch (Exception e) {
e.printStackTrace();
}
return responseMap;
}
public File convert(MultipartFile file)
{
File convFile = new File(file.getOriginalFilename());
try {
convFile.createNewFile();
FileOutputStream fos = new FileOutputStream(convFile);
fos.write(file.getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
return convFile;
}
I've had the same problem.
The best way for GC to delete the tmp file is to ensure your inputstream is closed after using it.
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException io) {
}
}
}
For some odd reason the inputstream stays open even after your rest service executes; this prevents the GC collect and delete it.
Looks like the problem lies with the fileoutstream logic to convert multipart file to file. Below is the workaround that i used to resolve this,
I replaced the conversion logic to,
public File multipartToFile(MultipartFile file) throws IllegalStateException, IOException
{
File tmpFile = new File(System.getProperty("user.dir") + File.separator + file.getOriginalFilename());
file.transferTo(tmpFile);
return tmpFile;
}
and for each multipart file iteration, i have put the converted file to a list and after I finished uploading the file, I iterated my list and deleted the file.
Cheers.

Compressing(zip) List of files with ZipOutPutStream Java

I`m trying to compress a list of Xml converted on Strings, save them in only one zip file and returning as a body of a POST on restful. But everytime I save the file I get the error "The archive is either in unknown format or damaged".
protected ByteArrayOutputStream zip(Map<String, String> mapConvertedXml) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
try {
for(Map.Entry<String, String> current : mapConvertedXml.entrySet()){
ZipEntry entry = new ZipEntry(current.getKey() + ".xml");
entry.setSize(current.getValue().length());
zos.putNextEntry(entry);
zos.write(current.getValue().getBytes());
zos.flush();
}
zos.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
return baos;
}
Can anyone help me with that?
To test your zip file please save it temporarily in your filesystem and open it manualy.
FileOutputStream fos = new FileOutputStream(pathToZipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
Then, you can use something like that to construct your Rest Server and return your file.
public static Response getFileLast(#Context UriInfo ui, #Context HttpHeaders hh) {
Response response = null;
byte[] sucess = null;
ByteArrayOutputStream baos = getYourFile();
sucess = baos.toByteArray();
if(sucess!=null) {
response = Response.ok(sucess, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition","attachment; filename = YourFileName").build();
} else {
response = Response.status(Status.NOT_FOUND).type(MediaType.APPLICATION_JSON).entity(new Error("Error downloading file.")).build();
}
return response;
}
You can test this Advanced Rest Client for example.

Spring MVC - Create a ZIP file in memory and let user download

I have a web project that
pulls data from Oracle DB
creates an XML file out of that data using a HashMap
ZIP it in memory
let users download the ZIP file.
note that I will not create a physical file prior to download.
I am done with 1-3. I can't seem to find a solution for the download part. I am using pure Spring MVC (as much as I can), Hibernate, MySQL.
HomeController.java
#RequestMapping(value="/doretrieve", method=RequestMethod.POST, produces="application/zip")
#ResponseBody
public ZipOutputStream doRetrieve(#RequestParam(value="calcgrouplist") String selectedCalcGroups, #RequestParam(value="env") String currentEnv){
ZipOutputStream zipCalgGroups = null;
try {
String[] cgs = calcGroupService.insertToArray(selectedCalcGroups);
for(String cg:cgs){
System.out.println("Calculation Group: " + cg);
}
Map startRetrieve = calcGroupService.startRetrieve(currentEnv, cgs);
if (startRetrieve != null ){
zipCalgGroups = calcGroupService.zipCalcGroups(currentEnv, startRetrieve);
} else {
return null;
}
} catch (NullPointerException e) {
e.printStackTrace();
}
return zipCalgGroups;
}
CalcGroupService.java
code to create zip file of the xml file/s
public ZipOutputStream zipCalcGroups(String database, Map startRetrieve) {
//Sort
//SortCalcGroupParameters sort = new SortCalcGroupParameters();
//sort.run(new File("\\" + database));
Map<String, byte[]> mapXmlFiles = startRetrieve;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos))
{
for (Map.Entry<String, byte[]> mapXmlFile:mapXmlFiles.entrySet()){
ZipEntry entry = new ZipEntry(mapXmlFile.getKey());
zos.putNextEntry(entry);
zos.write(mapXmlFile.getValue());
zos.closeEntry();
}
return zos;
} catch (IOException e) {
e.printStackTrace();
}
return null;
I was able to solve my own problem. Below are the edited methods:
HomeController:
#RequestMapping(value="/doretrieve", method=RequestMethod.POST, produces="application/zip")
#ResponseBody
public byte[] doRetrieve(HttpServletResponse response, #RequestParam(value="calcgrouplist")
String selectedCalcGroups, #RequestParam(value="env") String currentEnv){
try {
String[] cgs = calcGroupService.insertToArray(selectedCalcGroups);
for(String cg:cgs){
System.out.println("Calculation Group: " + cg);
}
//returns map of file name and xml
Map startRetrieve = calcGroupService.startRetrieve(currentEnv, cgs);
//set file name of the zipped calc group/s using selected environment
response.setHeader("Content-Disposition", "attachment; filename=" + currentEnv + ".zip");
if (startRetrieve != null ){
byte[] zipped = calcGroupService.zipCalcGroupsFromMemory(currentEnv, startRetrieve);
return zipped;
} else {
return null;
}
} catch (NullPointerException e) {
e.printStackTrace();
}
return null;
}
CalcGroupService:
public byte[] zipCalcGroupsFromMemory(String database, Map startRetrieve) {
Map<String, byte[]> mapXmlFiles = startRetrieve;
HttpServletRequest request = null;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos)) {
for (Map.Entry<String, byte[]> mapXmlFile : mapXmlFiles.entrySet()) {
byte[] xml = sortCalcGroup(mapXmlFile.getKey(), mapXmlFile.getValue());
zos.putNextEntry(new ZipEntry(mapXmlFile.getKey()));
//zos.write(mapXmlFile.getValue());
zos.write(xml);
zos.closeEntry();
}
zos.close();
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
The above code produces a nice zipped xml file/s.

JAX-RS - Wink - Correct way to read a file, using Wink Client

Related to this question which is about how to send a binary file to a client. I am doing this, actually my method #Produces("application/zip"), and it works well for the browser client. Now I'm trying to write some automated tests against the rest service, using the Wink client. So my question is not how to send the file to the client, but for how to consume the file, as a java rest client (in this case Apache Wink).
My resource method looks something like the below... Once I have a Wink ClientResponse object, how can I get the file from it so I can work with it?
#GET
#Path("/file")
#Produces("application/zip")
public javax.ws.rs.core.Response getFile() {
filesToZip.put("file.txt", myText);
ResponseBuilder responseBuilder = null;
javax.ws.rs.core.Response response = null;
InputStream in = null;
try {
in = new FileInputStream( createZipFile( filesToZip ) );
responseBuilder = javax.ws.rs.core.Response.ok(in, MediaType.APPLICATION_OCTET_STREAM_TYPE);
response = responseBuilder.header("content-disposition", "inline;filename="file.zip").build();
} catch( FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
return response;
The method that actually creates the zip file looks like this
private String createZipFile( Map<String,String> zipFiles ) {
ZipOutputStream zos = null;
File file = null;
String createdFileCanonicalPath = null;
try {
// create a temp file -- the ZIP Container
file = File.createTempFile("files", ".zip");
zos = new ZipOutputStream( new FileOutputStream(file));
// for each entry in the Map, create an inner zip entry
for (Iterator<Map.Entry<String, String>> it = zipFiles.entrySet().iterator(); it.hasNext();){
Map.Entry<String, String> entry = it.next();
String innerFileName = entry.getKey();
String textContent = entry.getValue();
zos.putNextEntry( new ZipEntry(innerFileName) );
StringBuilder sb = new StringBuilder();
byte[] contentInBytes = sb.append(textContent).toString().getBytes();
zos.write(contentInBytes, 0, contentInBytes.length);
zos.closeEntry();
}
zos.flush();
zos.close();
createdFileCanonicalPath = file.getCanonicalPath();
} catch (SecurityException se) {
se.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (zos != null) {
zos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return createdFileCanonicalPath;
}
You can consume it simply as input stream and use ZipInputStream to unzip it.
Here's example using Apache HTTP Client:
HttpClient httpclient = new DefaultHttpClient();
HttpGet get = new HttpGet(url);
get.addHeader(new BasicHeader("Accept", "application/zip"));
HttpResponse response = httpclient.execute(get);
InputStream is = response.getEntity().getContent();
ZipInputStream zip = new ZipInputStream(is);

Categories

Resources