Loading GZIP JSON file using AJAX - javascript

I have gzipped json file using below algorithm (from: java gzip can't keep original file's extension name)
private static boolean compress(String inputFileName, String targetFileName){
boolean compressResult=true;
int BUFFER = 1024*4;
byte[] B_ARRAY = new byte[BUFFER];
FileInputStream fins=null;
FileOutputStream fout=null;
GZIPOutputStream zout=null;
try{
File srcFile=new File(inputFileName);
fins=new FileInputStream (srcFile);
File tatgetFile=new File(targetFileName);
fout = new FileOutputStream(tatgetFile);
zout = new GZIPOutputStream(fout);
int number = 0;
while((number = fins.read(B_ARRAY, 0, BUFFER)) != -1){
zout.write(B_ARRAY, 0, number);
}
}catch(Exception e){
e.printStackTrace();
compressResult=false;
}finally{
try {
zout.close();
fout.close();
fins.close();
} catch (IOException e) {
e.printStackTrace();
compressResult=false;
}
}
return compressResult;
}
I am returning the JSON
response.setHeader("Content-Type", "application/json");
response.setHeader("Content-Encoding", "gzip");
response.setHeader("Vary", "Accept-Encoding");
response.setContentType("application/json");
response.setHeader("Content-Disposition","gzip");
response.sendRedirect(filePathurl);
or
request.getRequestDispatcher(filePathurl).forward(request, response);
Trying to access the JSON object using AJAX code as below:
$.ajax({
type : 'GET',
url : url,
headers : {'Accept-Encoding' : 'gzip'},
dataType : 'text',
The output I see is the binary data, not the decompressed JSON string. Any suggestion on how to make this work?
Note that the Browsers I am using (IE, Chrome, FF) supports gzip as all my static contents which are gzipped by Apache are rendered correctly.

By using:
response.sendRedirect(filePathurl);
You are creating another request/response. The headers you have defined are no longer associated with the file that actually gets sent.
Rather than sending a redirect, you need to load up your file and stream it in the same response.
Use Fiddler or another request viewer to see this.

Related

Java: Image upload with JavaScript - File is damaged, corrupted or too large

I am using Spring Boot as backend server and I have a JavaScript frontend.
For sending data between front- and backend I'm using the Axios library, which usually works pretty fine.
The Problem:
The image looks like this in the (Chrome) browser console:
It's a very very long alphanumeric string and that's what I send to the server with the following code:
static uploadFiles(files) {
const data = new FormData();
Object.keys(files).forEach(key => {
data.append("files", new Blob([files[key]], { type: 'image/jpeg' }));
});
const url = API_URL + "uploadFiles";
return axios.post(url, data, RestServices.getAuth({
"Content-Type": "multipart/form-data;boundary=gc0p4Jq0M2Yt08jU534c0p"
}));
}
I have no idea what the boundary thing does but it worked to receive a file in the backend tho...
On backend (spring) side I successfully receive an array of MultipartFiles:
#RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
#ResponseBody
public boolean uploadFiles(HttpServletRequest request, #RequestParam("files") MultipartFile[] files) throws IOException {
String filePath = Thread.currentThread().getContextClassLoader().getResource("assets/images/").getFile();
InputStream inputStream;
OutputStream outputStream;
for(MultipartFile file : files) {
File newFile = new File(filePath + file.getOriginalFilename() + ".jpg");
inputStream = file.getInputStream();
if (!newFile.exists() && newFile.createNewFile()) {
outputStream = new FileOutputStream(newFile);
int read;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
}
System.out.println(newFile.getAbsolutePath());
}
return true;
}
I've also tried it file.transferTo(newFile); instead of in- and outputstreams - which didn't work either.
After that I get the following output, which means that the image was saved successfully:
/path/to/blob.jpg
If I check the path where the file was uploaded, there is a file named blob.jpg, but if I open it, the windows photo viewer has the following problem:
I've opened the image before and after upload with notepad++:
Before upload:
I think this is a byte array, but If I open the image after upload I get exactly the output of the browser. This means it didn't get converted to a byte array (correct me if I'm wrong) and I believe that's why it's a corrupt image...
My questions are:
What's the problem?
How can I fix it?
I really tried everything which crossed my mind but I ran out of ideas.
Thanks for your help! :-)
I've read following *related* questions (but they **don't** have an answer):
[Question1][5], [Question2][6], and **many** more...
I've finally found an answer on my own!
I think the problem was that I used the e.target.result (which is used to show the image on the frontend) but insted I had to use the JS File object. The standard HTML 5 file input fields return those File objects (as I've read here).
The only thing I had to do now is to make a FormData object, append the File Object, set the FormData as Body and set the Content-Type header and that's it!
const data = new FormData();
data.append("files", fileObject);
return axios.post(url, data, {
"Content-Type": "multipart/form-data"
});
Those JS File Objects are recognized from Java as Multipart files:
#RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
#ResponseBody
public boolean uploadFiles(HttpServletRequest request, #RequestParam("files") MultipartFile[] files) {
boolean transferSuccessful = true;
for (MultipartFile file : files) {
String extension = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));
String newFileName = genRandomName() + extension; //set unique name when saving on server
File newFile;
File imageFolder = new File(imageBasePath);
//check if parent folders exist else create it
if(imageFolder .exists() || imageFolder .mkdirs()) {
while ((newFile = new File(imageFolder .getAbsolutePath() + "\\" + newFileName)).exists()) {
newFileName = genRandomName(); //generate new name if file already exists
}
try {
file.transferTo(newFile);
} catch (IOException e) {
e.printStackTrace();
transferSuccessful = false;
}
} else {
LOG.error("Could not create folder at " + imageFolder.getAbsolutePath());
transferSuccessful = false;
}
}
return transferSuccessful;
}
I hope this is helpful :)

Opening PDF served from servlet

Trying to send a PDF from a servlet to the client. The client sends a POST request through AJAX, and the request is handled by the servlet to generate a PDF and sends the PDF as a response to the client.
I've tried the options posted here to no avail (getting empty/un-openable pdfs): Opening PDF String in new window with javascript
Any help would be apprecieated!
So far, I can only get a formatted PDF String printing in the browser console using this code:
Java Servlet:
response.setContentType("application/pdf");
response.setHeader("Content-disposition","attachment; filename=ProgressReport.pdf");
response.setContentLength((int) pdfFile.length());
OutputStream out = response.getOutputStream();
FileInputStream in = new FileInputStream(pdfFile);
byte[] buffer = new byte[1024];
int length =0;
while ((length = in.read(buffer)) != -1){
out.write(buffer, 0, length);
System.out.println(buffer);
}
in.close();
out.flush();
JS
$.ajax({
url : "GenerateReport",
data : {
...
},
type : "POST",
success : function(result) {
console.log(result);
//window.open("data:application/pdf, " + encodeURI(result));
//download(result);
},
error : function(result) {
...
}
})
PDF String in Browser Console
%PDF-1.4 %����3 0 obj<</Filter/FlateDecode/Length 238>>streamx��QMO�#��x��(��D��!A�x�R��T�-�n��{�5LDB�e2�y�>2�l�Y$1�:a�i.�"�~f ...
I'm not 100% but your window.open is not the best as pop up blockers might prevent it as it's not a user action calling it's an AJAX response.
The better way would be the method outlined in this answer
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:attachment/text,' + encodeURI(result);
hiddenElement.target = '_blank';
hiddenElement.download = 'myFile.txt';
hiddenElement.click();
The other option is to use Base64 encoding and use "data:image/png;base64,"+result and in your C# you would need to create the buffer the size of the file and then base64 encode the entire buffer

Parsing a Gzip buffer in a Multiplatform (JS/Java + C#) Web platform

Let me explain a bit about my system. I have a third party software that communicates with a library/API (C#) of mine. This library communicates with my FrontEnd (JS) and, of course, this Front End communicates with my BackEnd (Java).
My problem is that I need my library/API to compress an XML that comes from third-party software, I pass it to the FrontEnd and it passes it to the BackEnd. But because this XML is too large, I need the library/API to compress it and the BackEnd unzip it. When the buffer reaches my BackEnd the decompressor jumps a "Not in GZip format" exception. I get the impression that the problem is in the Parse, but I can not find the solution. I would appreciate any help.
Library/API Code (C#):
public string operationXML(String xmlIN){
try
{
[...]
byte[] binaryData = Encoding.UTF8.GetBytes(xmlUtil.ObjectSerializer(objOUT, null));
//xmlUtil.ObjectSerializer returns a XML in String format
return Encoding.UTF8.GetString(CompressGZip(bynaryData));
}
catch()[...]
}
public byte[] CompressGZip(byte[] raw){
using (MemoryStream memory = new MemoryStream()){
using (GZipStream gzip = new GZipStream(memory, CompressionMode.Compress, true)){
gzip.Write(raw, 0, raw.Length);
}
return memory.ToArray();
}
}
FrontEnd (JS) Calling the library/API and Passing to BackEnd:
var response = petition("operationXML", responseObject.responseText);
conn.request({
url: gContextPath + "/MostradorGrid.json",
method: 'POST',
params: {
"action": "processXML",
"response": response,
"idUser": gIdUser
},
success: function (responseObject) {[...]
BackEnd Controller (Java):
case processXML:{
if (ParamMap.containsKey("idUser") && ParamMap.containsKey("response")) {
int idUser= NumberUtils.toInt(ParamMap.get("idUser")[0]);
byte[] xmlgzip= ParamMap.get("response")[0].getBytes("UTF8");
grid271 grid271 = (grid271) getWebApplicationContext().getBean("grid271");
response.getWriter().println(grid271.xmlprocess(xmlgzip, idUser));
}
}
BackEnd grid271.xmlprocess
[...]
byte[] temp = compressor.dGzip(xmlgzip);
String data= compressor.bytesToString( temp , "UTF_8", true);
[...]
BackEnd compressor
public byte[] dGzip(byte[] contentBytes) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
IOUtils.copy(new GZIPInputStream(new ByteArrayInputStream(contentBytes)), out);
} catch (IOException e) {
throw new RuntimeException(e);
}
return out.toByteArray();
}
In IOUtils.copy(new GZIPInputStream(new ByteArrayInputStream(contentBytes)), out); is when the Exception jumps: (java.Util.zip.zipException) java.util.zip.ZipException: Not in GZIP format
Any ideas?
Thanks in advance

Downloading a File Via Angular $.http POST

I am trying to download a zipped file that my server generates in my UI. I am at a loss as to how to get the file to download though. We have it setup so that we can download with window.open where we pass the url and it opens a blank page. We need to do a POST where it has a body now. I havent seen a way to send that along with a window.open. Does anyone have any pointers on how i can get access to the returned file?
Here is my current code...
#RequestMapping(method = RequestMethod.POST, value = "/archives/download", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Integer> getArchive(HttpServletResponse response, #RequestBody List<GeneratedReport> reportList) {
System.out.println(reportList.get(0).getFileLocation());
List<String> filesToDownload = new ArrayList<>();
reportList.stream().forEach(e -> filesToDownload.add(e.getFileLocation()));
filesToDownloadAndZip(response, filesToDownload, "zipped_file.zip");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=zipped_file.zip");
return new ResponseEntity<Integer>(200, HttpStatus.OK);
}
private void filesToDownloadAndZip(HttpServletResponse response, List<String> filesToDownload, String archiveFileName) {
try {
ByteArrayOutputStream baos = FileIO.CreateArchive(filesToDownload);
if (baos != null && baos.size() > 0) {
// Set the content type and attachment header.
response.addHeader("Content-disposition", "attachment;filename=" + archiveFileName);
response.setContentType("application/zip");
response.setContentLength(baos.size());
baos.writeTo(response.getOutputStream());
response.flushBuffer();
} else {
LOG.debug("File was null or size 0, try again");
}
} catch(Exception ex)
{
LOG.debug(ex.getMessage());
}
}
The js i have is.....
$http.post('api/archives/download', $scope.downloadItems)
.success(function(data, status, headers, config) {
//I dont know what to do here..... :(
})

Uploading PDF from jsPDF with AJAX using binary data

I am attempting to pass a PDF I have generated on frontend javascript using jsPDF to a Spring Framework MVC backend. Below is the front end code I have written:
var filename = "thefile";
var constructURL = '/daas-rest-services/dashboard/pdfPrintUpload/' + filename;
var url = restService.getUrl(constructURL);
var fileBytes = btoa(pdf.output());
$http.post(url, fileBytes).success(function(data) {
console.log(data);
})
.error(function(e, a) {
console.log(e);
console.log(a);
});
The pdf variable has been generated properly and can confirm is opens correctly when calling pdf.save("filename"). Below is the Java code which has been written on the Spring MVC backend for this call:
#RequestMapping(method = RequestMethod.POST, value = "/pdfPrintUpload/{documentName}")
public #ResponseBody String postPrintDocument(#PathVariable String documentName, #RequestParam byte[] fileBytes) {
String methodName = "postPrintDocument";
if(logger.isLoggable(Level.FINER)){
logger.entering(CLASS_NAME, methodName);
}
String check;
if(fileBytes != null){
check = "not null";
} else {
check = "null ";
}
//Decoding the bytestream
//Save to file location
//return file location
String returnValue = "HI " + documentName + " " + check;
if (logger.isLoggable(Level.FINER)) {
logger.exiting(CLASS_NAME, methodName);
}
return returnValue;
}
Each time I make a request, I am getting 400 Errors telling me:
Error 400: Required byte[] parameter 'fileBytes' is not present
I can confirm in the request payload that a large amount of data is being transmitted, however the backend does not seem to want to accept the parameter.
The purpose of doing this is that I want to be able to get the data from the pdf and then decode it on the backend so I can later publish the pdf to a location on the server. Is there something I am missing in my code for these requests to keep failing, and is there an easier more efficient way to achieve this functionality?
The solution was changing the #RequestParam to #RequestBody. #RequestParam is a parameter which is sent in the path.
#RequestParam vs #PathVariable
Try using ng-file-upload. The link and the examples are available on the link
ng-file-upload
for the sever side code try using this
#RequestMapping(value = "/pdfPrintUpload")
#ResponseBody
public void postPrintDocument(#RequestParam("file") MultipartFile file) {
InputStream is = file.getInputStream();
OutputStream os = new FileOutputStream(/*path to save file*/);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0)
os.write(buffer, 0, length);
is.close();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}

Categories