File upload with Jersey, File not being decoded - java

I've been trying to do file upload to a server from Javascript to a Jakarta REST service. The file is not getting decoded and I can't find what's wrong.
The upload code I'm using in javascript is basic FormData upload (vanillajs):
var url=.....
var xhttp = new XMLHttpRequest();
var formData = new FormData();
xhttp.onreadystatechange = () => {
if (this.readyState == 4)
{
if (this.status == 200)
{
if (successCallback != null) successCallback(this.responseText);
}
else
{
if (errorCallback != null) errorCallback(this.status, this.statusText);
}
}
};
xhttp.open("POST", url, true);
formData.append("file", file);
xhttp.send(formData);
The way I'm getting the file is to use a file input, and when the user loads an image, I load a picture of it and use that (React):
const [imgSrc, setImgSrc] = React.useState('');
const loadImage=(e)=>{
var file=e.target.files[0];
var reader = new FileReader();
reader.onload = (e2)=>{
setImgSrc(e2.target.result);
}
reader.readAsDataURL(file);
}
<input type="file" accept="image/*" onChange={(e)=>loadImage(e)} />
That seems to work fine. When I look at the request headers I see:
Content-Type: multipart/form-data; boundary=....
and the payload starts with:
file: data:image/jpeg;base64,/9j/...
Then the REST service I'm using to receive this is:
#POST
#Path("/submit")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadImage(
#FormDataParam("file") File fileBody,
#FormDataParam("file") FormDataContentDisposition metadata)
{
// set up S3 bucket, all that works fine
s3.putObject("mybucket", "pick_a_name", new FileInputStream(fileBody), null);
}
and that works, BUT, the file content is the literal content of the payload:
data:image/jpeg;base64,/9j/...
So it's not being recognized as a base 64 encoded file content and being decoded.
I've gone through various iterations and questions, including variations that result in 400 or 415 errors, but as far as I can tell these things look like they should line up.
A few more data points:
Tomcat 10
Jakarta 3.0.0
Jersey 3.0.3 (and jersey media multipart 3.0.3)
Java 8
Any suggestions would be appreciated. Thanks!

I'd like to get a "real" (what's the correct problem and solution) answer, but in the meantime I went with a hack:
InputStream outStream = hackBase64Decode(fileBody);
s3.putObject("mybucket", "pick_a_name", outStream, null);
private InputStream hackBase64Decode(File fileBody) throws Exception
{
FileInputStream fs = new FileInputStream(fileBody);
int ch;
do {
ch = fs.read();
} while (ch != ',');
return Base64.getDecoder().wrap(fs);
}
The best that can be said for that is it works. Again, though, would prefer a real answer.

Related

Jersey HTTP POST method corrupting non-text files

I have a HTTP POST method that works fine if I upload text files. But if I try to upload a word document, pdf, zip, gzip, etc... the files that are uploaded get corrupted in the process. I'm using Postman to send the request. I do a "POST" method, enter the url, add headers (tried all sorts of headers and it really does not change anything so now I don't have any entered), and then on the body I select "formdata" and select the file. I really just need to fix this to be able to support files that end in .csv.gz and .csv. Currently, csv is fine but the .csv.gz is the type that is corrupting. I tried other non-text files as well just to see what happens and they corrupt too. I cannot figure out if there is some encoding, filter, etc... that is causing this to happen that I can remove or some setting I need to apply. Or if there is some other way to handle this with jersey so the non-text files stay the same as the original file.
My application is running Spring v1.5.3 and Jersey 2.25.
#Override
public Response uploadTopicFile(String topic, FormDataMultiPart formDataMultipart) throws Exception {
List<BodyPart> bodyParts = formDataMultipart.getBodyParts();
// Getting the body of the request (should be a file)
for (BodyPart bodyPart : bodyParts) {
String fileName = bodyPart.getContentDisposition().getFileName();
InputStream fileInputStream = bodyPart.getEntityAs(InputStream.class);
String uploadedFileLocation = env.getProperty("temp.upload.path") + File.separator + fileName;
this.saveFile(fileInputStream, uploadedFileLocation);
String output = "File uploaded to : " + uploadedFileLocation;
log.debug(output);
}
return Response.status(201).build();
}
private void saveFile(InputStream uploadedInputStream, String serverLocation) {
try {
// Create the output directory
Files.createDirectories(Paths.get(serverLocation).getParent());
// Get the output stream
OutputStream outputStream = new FileOutputStream(new File(serverLocation));
int read = 0;
byte[] bytes = new byte[1024];
// Loop through the stream
while ((read = uploadedInputStream.read(bytes)) != -1) {
// Output to file
outputStream.write(bytes, 0, read);
}
// Flush and close
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
There was a filter causing the corruption. Filter was updated and issue resolved.

Open a pdf file using javax.ws and angular

I am trying to open a PDF file, saved in the server, using a Java Restful service and angularjs.
My code for the service in Java is:
#GET
#Path("/getPDF")
#Produces("application/pdf")
public Response getPDF() throws FileNotFoundException {
File file = new File("/path/to/file.pdf");
FileInputStream fileInputStream;
fileInputStream = new FileInputStream(file);
long contentLength = file.length();
ResponseBuilder responseBuilder = Response.ok((Object) fileInputStream);
responseBuilder.type("application/pdf");
responseBuilder.header("Content-Disposition", "inline; filename=file.pdf");
responseBuilder.header("Content-Length", contentLength);
responseBuilder.header("charset", "utf-8");
return responseBuilder.build();
}
Once the Response is returned I handle the request with angularjs:
MyJavaService.getPDF().success(function(data){
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
$window.open(fileURL);
});
The result is that a new window opens, with the same number of pages like the PDF file (correct), but the content is not displayed and all the pages are white (not correct).
Does anyone have any clue of what am I doing wrong?
I'm assuming you're using a $http.get() call.
You probably having something along the lines of:
$http.get(<URL>, {params: <stuff>})
You need to tell angular that the responseType is an arrayBuffer like so:
$http.get(<URL>, {responseType: 'arraybuffer', params: <stuff>})
Don't hesitate to tell me my assumptions are incorrect.
p.s.: try to avoid using .success() and instead use .then(). The former is deprecated 1.4+ and completely removed in 1.6+

Zip file getting downloaded without .zip extension in Chrome through Java

I am trying to download a zip file from a fixed location present in server.
In my Rest method , I am just passing the file name from client (browser) .
(Please see below code ).
In my Rest method I am sending the zip file to the client.
The file gets downloaded on the browser without any issue.
My Issue is that the zip file gets downloaded on browser without .zip extension.
#RequestMapping(value = "/zip/{filePath}", method = RequestMethod.GET)
public #ResponseBody void downloadZip(#PathVariable("filePath") String filePath, HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletContext context = request.getServletContext();
File downloadFile = new File(filePath);
FileInputStream inputStream = new FileInputStream(downloadFile);
// get output stream of the response
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[(int) downloadFile.length()];
int bytesRead = -1;
// write bytes read from the input stream into the output stream
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
// get MIME type of the file
String mimeType = context.getMimeType(fullPath);
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
System.out.println("MIME type: " + mimeType);
// set content attributes for the response
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.length());
response.setHeader("Content-Disposition",
String.format("attachment; filename=\"%s\"", downloadFile.getName()));
logger.error("Filename = " + downloadFile.getName());
inputStream.close();
outStream.close();
}
PS: The file gets downloaded on some machine with ZIP and in some machine without ZIP. I have tested only on chrome (as per client requirement).
I think, there is an issue with the Chrome settings which I need to look upon (just a guess).
Can someone help upon this?
Thanks in advance....
Change the order between setting the response headers and shoving the file down the output stream - after all, the headers need to leave first.
[Edited]
"Why setting HttpServletResponse in starting effects the code."
Well, simple: the client is supposed to receive instructions of what to do with the payload by interpreting the HTTP response headers. If those are not set in the beginning, sending those headers at the end of the transmission comes too late. And this assumes the HttpServletResponse will actually send those headers when invoked with setHeader, which is a big assumption - I suspect those headers will not actually be sent after calling response.getOutputStream - it is unlikely the response will buffer the entire payload to wait for the caller to specify those headers.

Sending pdf data through rest api inside json

I have made a webservice that send multiple pdfs as response to client using mutlipart/formdata but as it happens one of the client is salesforce which does not support mutlipart/ formdata.
They want a json in response like -
{ "filename": xyzname,
"fileContent": fileContent
}
I tried encoding data in Base64 using apache codec library but pdf on client side seems to get corrupted and I am unable to open it using acrobat.
Please find code below -
import org.apache.commons.io.FileUtils;
//------Server side ----------------
#POST
#Consumes(MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
#Path("somepath")
public Response someMethod(someparam) throws Exception
{
....
JSONArray filesJson = new JSONArray();
String base64EncodedData = Base64.encodeBase64URLSafeString(loadFileAsBytesArray(tempfile));
JSONObject fileJSON = new JSONObject();
fileJSON.put("fileName",somename);
fileJSON.put("fileContent", base64EncodedData);
filesJson.put(fileJSON);
.. so on ppopulate jsonArray...
//sending reponse
responseBuilder = Response.ok().entity(filesJson.toString()).type(MediaType.APPLICATION_JSON_TYPE) ;
response = responseBuilder.build();
}
//------------Client side--------------
Response clientResponse = webTarget.request()
.post(Entity.entity(entity,MediaType.MULTIPART_FORM_DATA));
String response = clientResponse.readEntity((String.class));
JSONArray fileList = new JSONArray(response);
for(int count= 0 ;count< fileList.length();count++)
{
JSONObject fileJson = fileList.getJSONObject(count);
byte[] decodedBytes = Base64.decodeBase64(fileJson.get("fileContent").toString());
outputFile = new File("somelocation/" + fileJson.get("fileName").toString() + ".pdf");
FileUtils.writeByteArraysToFile(outputFile, fileJson.get("fileContent").toString().getBytes());
}
-------------------------------
Kindly advise.
Yes so the problem was with the client.
while decoding we should use
byte[] decodedBytes = Base64.decodeBase64(fileJson.getString("fileContent"));
rather than
byte[] decodedBytes = Base64.decodeBase64(fileJson.get("fileContent").toString());
Since encoded data.toString() yields some think else
Also replaced encodeBase64URLSafeString with encodeBase64String
Well quite a simple solution :)
We are doing the same, basically sending PDF as JSON to Android/iOS and Web-Client (so Java and Swift).
The JSON Object:
public class Attachment implements Serializable {
private String name;
private String content;
private Type contentType; // enum: PDF, RTF, CSV, ...
// Getters and Setters
}
And then from byte[] content it is set the following way:
public Attachment createAttachment(byte[] content, String name, Type contentType) {
Attachment attachment = new Attachment();
attachment.setContentType(contentType);
attachment.setName(name);
attachment.setContent(new String(Base64.getMimeEncoder().encode(content), StandardCharsets.UTF_8));
}
Client Side Java we create our own file type object first before mapping to java.io.File:
public OurFile getAsFile(String content, String name, Type contentType) {
OurFile file = new OurFile();
file.setContentType(contentType);
file.setName(name);
file.setContent(Base64.getMimeDecoder().decode(content.getBytes(StandardCharsets.UTF_8)));
return file;
}
And finally:
public class OurFile {
//...
public File getFile() {
if (content == null) {
return null;
}
try {
File tempDir = Files.createTempDir();
File tmpFile = new File(tempDir, name + contentType.getFileEnding());
tempDir.deleteOnExit();
FileUtils.copyInputStreamToFile(new ByteArrayInputStream(content), tmpFile);
return tmpFile;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
In my REST application with PHP:
  1. Send the data encoded in base64 $data = base64_encode($data) to REST.
  2. Before writing the file, I decode $data = base64_decode($data).
  3. Therefore, when the file is downloaded, it is already in the correct format.
I would change from using "Safe" to just using "string". So change:
encodeBase64URLSafeString(...)
to:
encodeBase64String(...)
The reason is that the "safe" versions actually change the content to preserve URLs before encrypting - I'm totally uncertain what that would do to a PDF, but suspect it is the source of your problem.
If that doesn't do it for you, I suggest encrypting/decrypting right on the server (or a separate test app) and comparing the results while you try to work it out. That way you can see if what you are doing is working, but don't have to go through the whole "start the server, start the client, connect..." process each time, and it will speed your debugging.

ajax response as pdf, how to show it?

I have a web service that generate a pdf. In my GAE application I have a button, when i click i use an ajax's function.
$('#test').click(function(){
$.ajax({
url: 'provaws.do',
type: 'get',
dataType: 'html',
success : function(data) {
}
});
});
this is the method in java that's call ws, using UrlFetch:
#RequestMapping(method = RequestMethod.GET, value = PROVAWS_URL)
public void prova(HttpServletRequest httpRequest, HttpServletResponse httpResponse, HttpSession httpSession) throws IOException{
try {
URL url = new URL("http://XXXXX/sap/bc/zcl_getpdf/vbeln/yyyyyy");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Authorization","Basic " + Base64.encodeBase64String(("username:password").getBytes()));
connection.setConnectTimeout(60000);
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// OK
ByteArrayOutputStream bais = new ByteArrayOutputStream();
InputStream is = null;
try {
is = connection.getInputStream();
byte[] byteChunk = new byte[4096];
int n;
while ( (n = is.read(byteChunk)) > 0 ) {
bais.write(byteChunk, 0, n);
}
}
catch (IOException e) {
}
finally {
if (is != null) { is.close(); }
}
httpResponse.setContentType("application/pdf");
httpResponse.setHeader("content-disposition","attachment; filename=yyyyy.pdf");
httpResponse.getOutputStream().write(bais.toString().getBytes("UTF-8"));
httpResponse.getOutputStream().flush();
}
....
}
With Firebug i see the repsonse:
%PDF-1.3
%âãÏÓ
2 0 obj
<<
/Type /FontDescriptor
/Ascent 720
/CapHeight 660
/Descent -270
/Flags 32
/FontBBox [-177 -269 1123 866]
/FontName /Helvetica-Bold
/ItalicAngle 0
....
What i need to set in ajax's function to show the pdf?
Thanks in advance
I don't know Java well, but in my understanding your mechanism may not be right.
Here are my corrections:
Instead of sending files in stream, the server-side code(JAVA) should generate the pdf at backend, put the file in file system, and then return the URI of file to Ajax response.
For Ajax code, it get the url from server, then show the new url in DOM. Then user can follow this link to read/download PDF.
Side note:
I checked further that there are methods for streaming data by Ajax, though jQuery's ajax() can't handle that. But I think for a PDF file rendering, streaming is overkill.
Refs: jquery ajax, read the stream incrementally?, http://ajaxpatterns.org/HTTP_Streaming#In_A_Blink*

Categories

Resources