Uploading PDF from jsPDF with AJAX using binary data - javascript

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();
}
}

Related

Consume a C# API that returns file or byte[] from javascript as image

I've the following code that returns an image in C# Web API. So far so good but I can't map it to an image.
[HttpPost]
[Route("GetImage")]
[ValidateAntiForgeryToken]
public FileResult GetImage(string name)
{
if (string.IsNullOrEmpty(name)) {
return File("imgs/nodisponible.jpg", "image/jpg");
}
var imagen = REST.GetImage(name);
return new FileContentResult(imagen, "image/jpeg");
}
So on the javascript side I made:
getimage(word: any): Observable<any> {
return this.http.post<ArrayBuffer>(this.baseUrl + 'library/getimage', word, {
headers: this.headers,
withCredentials: true,
});
}
and then to show the image:
const iname = JSON.stringify(this.consultadetalle?.cover_url);
this.dataService.getimage(iname).subscribe((responses) => {
$('#imagencover').attr('src', "data:image/png;base64," + responses);
});
but the console says:
Http failure during parsing for
https://localhost:44476/library/getimage"
How can I solve this?
Update:
As I said, I'm using Web API, I don't have the view in C#, only javascript with angular. In C# I've only the controllers.
You may need to convert the file to bytes first. I successfully used this, in a project similar to yours, to convert the image file to a byte array.
public static byte[] ConverToBytes(HttpPostedFileBase file)
{
var length = file.InputStream.Length; //Length: 103050706
byte[] fileData = null;
using (var binaryReader = new BinaryReader(file.InputStream))
{
fileData = binaryReader.Renter code hereeadBytes(file.ContentLength);
}
return fileData;
}
after which I loaded that information into a Viewbag and converted that to Base64
var ImageBytes = ConverToBytes(ImageFromApi);
var displayImage= "data:" + imagePulled.FileType + ";base64," + Convert.ToBase64String(ImageBytes);
ViewBag.FileBytes = displayImage;
then displayed it in a view using this.
<object data="#ViewBag.FileBytes" id="imageDisplayFrame" style="width:100%; min-height:600px; border:1px solid lightgrey; object-fit:contain;" #zoom="200" frameBorder="1" type="#ViewBag.FileType" />

How do I parse an image sent from Retrofit API from Android (multipart/form) in Python's Flask

I am sending my image as a part of Form Data through Retrofit API. There are no issues loading the image. I am trying to get this image in a Python Flask server.
My python code is not responding the expected way. I have tested my Python code with a JavaScript frontend application and the python server responds as expected. I believe the issue is parsing the multipart/form file which I receive from Android.
There are no network issues, I am able to log the requests. The detectFace() function is not responding as expected for the same image sent through both clients, VueJs and Android.
Any ideas will be appreciated.
Here is the android code for uploading:
private void sendImageToServer() {
File imageFile = loadImageFromStorage(tempImagePath);
RequestBody reqBody = RequestBody.create(MediaType.parse("image/jpeg"), imageFile);
MultipartBody.Part partImage = MultipartBody.Part.createFormData("file", "testImage", reqBody);
API api = RetrofitClient.getInstance().getAPI();
Call<TestResult> upload = api.uploadImage(partImage);
upload.enqueue(new Callback<TestResult>() {
#Override
public void onResponse(Call<TestResult> call, Response<TestResult> response) {
if(response.isSuccessful()) {
TestResult res = response.body();
String jsonRes = new Gson().toJson(response.body());
String result = res.getResult();
Log.v("REST22", result);
}
}
#Override
public void onFailure(Call<TestResult> call, Throwable t) {
Log.v("REST22", t.toString());
Toast.makeText(MainActivity.this, t.toString(), Toast.LENGTH_SHORT).show();
}
});
}
Here is Python code:
#app.route('/detectFaces/', methods=['POST'])
def detectFaces():
img = request.files.get('file')
print('LOG', request.files)
groupName = 'random-group-03'
result = face.detectFaces(img, groupName)
print('RESULT', result)
return {'result' : result[0]}
VueJs - alternate working frontend (REST client):
sendImage(img) {
console.log(img)
var form = new FormData();
form.append('file', img, 'testImage')
axios.post(this.baseUrl + 'detectFaces/?groupName=random-group-03', form)
.then(res => {console.log(res.data); this.log = 'Detected face ids: \n ' + res.data.result});
}

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

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

Loading GZIP JSON file using AJAX

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.

Categories