Downloading a File Via Angular $.http POST - javascript

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..... :(
})

Related

Unable to open pdf file in new tab

I have one requirement where in html page if user click on button then javascript function gets called and in that function ajax call will fetch content of the pdf file from server.
Please find the rest controller as below
#RequestMapping(value = UriMapping.GET_PDF_PATH, method = RequestMethod.POST)
public #ResponseBody WebServiceResponse getPdfPath(HttpServletRequest req,
#RequestParam String fileName, HttpServletResponse response) {
WebServiceResponse res = new WebServiceResponse();
FileInputStream fis = null;
try {
if(!CommonUtil.isBlank(fileName)) {
String filePath = FieldConstant.PDF_PATH + fileName;
File f = new File(filePath);
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "inline;filename=" + f.getName() );
fis = new FileInputStream(f);
DataOutputStream os = new DataOutputStream(response.getOutputStream());
response.setHeader("Content-Length", String.valueOf(f.length()));
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) >= 0) {
os.write(buffer, 0, len);
}
fis.close();
} else {
res.setSucess(false);
res.setReturnMessage("Something Went Wrong While opening file path !");
}
} catch (Exception e) {
LOGGER.error(e.toString());
res.setSucess(false);
res.setReturnMessage("Something Went Wrong While opening file path !");
}
LOGGER.info("Response" + res.toString());
return res;
}
FieldConstant.PDF_PATH is fixed path at server where all pdf files resides.
Below is the client side jquery function where in I have used window.open() function to open pdf in new tab.
function test(count){
var fileName = pdfGlobal[count].name;
if(fileName != undefined && fileName != "") {
var param = {
"fileName" :fileName
}
$.ajax({
url : '../content/getPdfPath',
type : 'post',
dataType : "json",
data : param,
error : function(error,jqXHR, exception) {
errorMessage(exception);
},
success : function(data) {
if (data) {
window.open(data,'_blank');
} else{
errorMessage(data.returnMessage);
}
}
});
}
}
I am getting parsing error like below
Now as the error suggest I found the % in first place of the response !
Please help me with this.. I know this is not a big issue but I am confused about what goes wrong. Simply not able to find the root cause ...
Thanks in advance.
The ajax is expecting a JSON in return, there is no need for you to use ajax, you can just use window.open, sending fileName by get
window.open('../content/getPdfPath?fileName='+fileName,'_blank');
So you have to change your controller to
#RequestMapping(value = UriMapping.GET_PDF_PATH, method = RequestMethod.GET)

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 :)

Catch exception from controller in Window.open using Javascript

I have a controller, which is used to flush the bytes, which inturn will generate the PDF on the client side. But, if an exception occurs, I want to catch that and show some appropriate message to the user.
I tried Ajax, but Ajax uses only JSON, String or XML for message exchange format. How can I handle this conditon?
I have to generate the pdf in success case or catch the exception. Here is my controller and javascript code
try {
MyUtil.generatePdf(response, documentBytes, "DU"); // --> This method will flush the bytes
} catch (Exception e) {
result.setStatus("EXCEPTION OCCURED.");
}
return result;
Generate pdf method
public static void GeneratePdf(HttpServletResponse response, byte[] documentBytes,
String fileName) {
response.setHeader("Content-Disposition", "inline;filename=" + fileName + ".pdf");
response.setContentType("application/pdf");
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, postcheck=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setContentLength(documentBytes.length);
ServletOutputStream out = null;
try {
out = response.getOutputStream();
out.write(documentBytes);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Javascript code
try {
window.open("http://localhost:8080/INQ/CBU/5559901410151HELMPFN");
} catch (e) {
console.log(e);
}
I do not think that you can do this with window.open().
My suggestion is to use servlet API in a way that you either download the PDF or in case of an error show an error page to the user.
I have some test code for exactly the scenario in which either the PDF is written out OR an error page is sent to the user with an error message.
The clue is that an exception is caught and in that case an error message is sent out to the user using a different encoding.
servlet code
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#WebServlet("/download")
public class PDFServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String fileName = "asset_report1.pdf"; // Dummy file
// This throws sometimes an exception.
byte[] documentBytes = MyUtil.generatePdf(fileName);
response.setHeader("Content-Disposition", "inline;filename=" + fileName);
response.setContentType("application/pdf");
expireCache(response);
response.setHeader("Pragma", "public");
response.setContentLength(documentBytes.length);
ServletOutputStream out = response.getOutputStream();
out.write(documentBytes);
out.flush();
} catch (IOException e) {
expireCache(response);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"The PDF file could not be retrieved.");
}
}
private void expireCache(HttpServletResponse response) {
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, postcheck=0, pre-check=0");
}
}
This works out pretty well. Either you get the PDF downloaded OR you get an error page. You will need to eventually work on you error page to make it look pretty, but the default error page is a start.
Sometimes you see the PDF and in case of an error the default error page:
In your case its best to get the pdf response as arraybuffer in angular side and store it as a blob and then download it. You can do it making a post rquest to the server. And add a error block in your angular code.
Server Side Code..
List<PhysicalCountEntityTo> salaryList = exportNonDashBoardReportManager.fetchSalaryDetails(payloadBean);
HashMap<String, Object> parametersPDF = new HashMap<String, Object>();
JasperPrint jasperPrint = null;
JRBeanCollectionDataSource beanCollectionDataSource = null;
beanCollectionDataSource = new JRBeanCollectionDataSource(salaryList);
String reportPath = httpServletRequest.getServletContext().getRealPath("//WEB-INF//JasperReports//Salary.jasper");
jasperPrint = JasperFillManager.fillReport(reportPath, parametersPDF, beanCollectionDataSource);
httpServletResponse.addHeader("Content-disposition","attachment; filename=Salary.pdf");
ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint, servletOutputStream);
Angular Side Code -- you can use this code under your ajax request as you're using javascript
exportPDF(event){
this.showPageSpinner=true;
this.httpRestClient.fetchPDF("download_salary_report", this.payloadBean).subscribe(
response => {
var blob = new Blob([response], {type: 'application/pdf'});
var ua = window.navigator.userAgent;
var msie = ua.indexOf('MSIE');
var trident = ua.indexOf('Trident/');
var edge = ua.indexOf('Edge/');
if(msie > 0 || trident > 0 || edge > 0){
window.navigator.msSaveOrOpenBlob(blob,'Salary.pdf');
}
else if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1){
var link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = "Salary.pdf";
document.body.appendChild(link);
link.click();
window.setTimeout(function() {
URL.revokeObjectURL(link.href);
document.body.removeChild(link);
}, 0);
}
else{
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="Salary.pdf";
link.click();
}
this.showPageSpinner=false;
},
error=>{
// show your message here
});
}
Important - In request header add response type to 'arraybuffer' as 'json' otherwise it won't work
fetchPDF(url: string,data): Observable<any> {
this.getCredentials();
const authHeaders = this.createBasicAuthorizationHeader(this.credentials);
return this.http.post(this.getApiUrl(url),data,{headers: authHeaders,'responseType' : 'arraybuffer' as 'json'})
}
It works in IE,Mozilla and Crome..

Efficient way to upload an image to PHP from Android

I am uploading an image to PHP from Android by converting it to the byte array. After that I simply submit it using POST method.
On server side I do the reverse of what I am doing at client (Android app) side.
I was wondering if there is any other good/efficient/smart way to do this.
Note: I only have to use only PHP/JS/HTML and obviously Java at client side.
One of the most efficient ways is doing it using Volley, so make sure to include it in your gradle:
compile 'com.android.volley:volley:1.0.0'
I personally use Universal Image Loader which is included automatically when you include Volley. Since you haven't put any code that you tried, i'll give you some examples. In your activity that you're trying to upload the image, create a button. Add this code when that button is clicked:
Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
i.setType("image/*");
startActivityForResult(i, Constants.VALUE_BROWSE_IMAGE_REQUEST);
This will open the gallery in your phone to browse for an image. Declare a variable at the top of your activity:
private Bitmap mBitmap;
After you choose the image you want to upload from your gallery, write this code:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == Constants.VALUE_BROWSE_IMAGE_REQUEST &&
resultCode == RESULT_OK &&
data != null) {
try {
// Get the photo URI data
Uri filePath = data.getData();
// Get the Bitmap from Gallery
mBitmap = decodeBitmap(filePath, this);
} catch (IOException e) {
Toast.makeText(this, "Could not open picture.", Toast.LENGTH_SHORT).show();
}
}
}
Now that you have the bitmap of the chosen image, you need to convert that bitmap into base64 string so that Volley can be able to upload it:
// Before uploading the selected image to the server, we need to convert the Bitmap to String.
// This method will convert Bitmap to base64 String.
private String getStringImage(Bitmap bmp) {
ByteArrayOutputStream b = new ByteArrayOutputStream();
// This part handles the image compression. Keep the image quality
// at 70-90 so you don't cause lag when loading it on android
// (0-low quality but fast load, 100-best (original) quality but slow load)
bmp.compress(Bitmap.CompressFormat.JPEG, 80, b);
byte[] imageBytes = b.toByteArray();
String encodedImage = Base64.encodeToString(imageBytes, Base64.DEFAULT);
return encodedImage;
}
Finally you can start uploading the image:
private void uploadImage() {
StringRequest stringRequest = new StringRequest(
Request.Method.POST,
"URL_TO_YOUR_WEB_API",
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(), "Failed to upload image.", Toast.LENGTH_SHORT).show();
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
// Converting Bitmap to String
String image = getStringImage(mBitmap);
// Create parameters
Map<String, String> params = new Hashtable<>();
// Add parameters
params.put("action", "YOUR_BACKEND_KEY1");
params.put("...", image);
// Returning parameters
return params;
}
};
// Creating a Request Queue
RequestQueue requestQueue = Volley.newRequestQueue(this);
// Adding request to the queue
requestQueue.add(stringRequest);
}
Make sure to replace the parameter strings with however you have created your backend in php.
Example URL:
http://yoursite.com/api.php?action=uploadimage&imagebase=adwtgiuewohnjsoiu&caption=somecaptiontext
Then the parameters in android would be:
params.put("action", "uploadimage");
params.put("imagebase", image);
params.put("caption", "somecaptiontext");

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