How to attach a FileItem to a HTTP POST request? - java

I have created a webpage (JSP & AngularJS) that contains a form allowing the user to attach a file and send it to the server (Java Servlet). The server will then take that file and forward it to an API by attaching it to a HTTP POST request.
The code I have at the moment within the JSP File and the AngularJS Controller appears to be working correctly. Once the file is sent from the webpage to the server, I can then access some of the file details (field name and size but not content type or file name) in the Java Servlet and print them out via System.out.println().
The problem I am facing at the moment is trying to find a way how to attach the FileItem (attachment) to the HttpPost (postRequest).
I have read a number of examples online on how files are uploaded, however these examples always assume the file will be stored on a disk on the server instead of being forwarded elsewhere.
This is my current code (the problem seems to be in the Java Servlet section):
JSP File:
<form name="issueForm">
<input id="attachment" class="form-control" type="file" data-ng-model="attachment"/>
<button type="submit" data-ng-click="setAttachment()">Create Issue</button>
</form>
AngularJS Controller:
app.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function() {
scope.$apply(function() {
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
$scope.setAttachment = function()
{
var attachment = $scope.attachment;
var fd = new FormData();
fd.append('attachment', attachment);
$http({
url: 'IssueAttachment',
method: 'POST',
transformRequest: function(data, headersGetterFunction) { return data; },
headers: { 'Content-Type': undefined },
data: fd
})
.success(function(data, status) { alert("Success: " + status); })
.error(function(data, status) { alert("Error: " + status); });
}
Java Servlet:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
FileItem attachment = null;
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) { System.out.println("Not Multipart Content!"); }
else {
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = null;
try {
items = upload.parseRequest(new ServletRequestContext(request));
} catch (FileUploadException e) { e.printStackTrace(); }
try {
//Get attachment and print details
//This section prints "attachment", 9, null, null in that order).
attachment = (FileItem) items.get(0);
System.out.println("Field Name: " + attachment.getFieldName());
System.out.println("Size: " + attachment.getSize());
System.out.println("Content Type: " + attachment.getContentType());
System.out.println("File Name: " + attachment.getName());
} catch (Exception e) { e.printStackTrace(); }
//Create a HTTP POST and send the attachment.
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost(API_URL);
MultipartEntityBuilder entity = MultipartEntityBuilder.create();
entity.addPart("attachment", new FileBody(attachment)); //THE ERROR OCCURS HERE.
postRequest.setEntity(entity.build());
try {
HttpResponse response = httpClient.execute(postRequest);
} catch (IOException e) { e.printStackTrace(); }
}
}

Ended up using the following:
FileItem file = (FileItem)items.get(0);
//Create a temporary file.
File myFile = File.createTempFile(base, extension);
//Write contents to temporary file.
file.write(myFile);
/**
* Do whatever you want with the temporary file here...
*/
//Delete the temporary file.
myFile.delete(); //-OR- myFile.deleteOnExit();

Related

How to specify filename when using MailGun API

I am currently in the process of integrating MailGun into one of my applications. For my use cases I need to be able to send out attachments. So far, I have been able to send out attachments just fine but my problem is that I am unable to specify the attachment's name. Their documentation found here specifies that the attachment part should be added when including attachment, but does not state how to specify the file's name.
For reference I am using Spring's RestTemplate as my client and I am reading the file as a base64 encoded string which is then trasnformed into a ByteArrayResource. For reference my code is this:
#Override
public EmailDocument sendEmail(EmailDocument email) {
var properties = propProvider.findFor(email.getCompany());
var parts = new LinkedMultiValueMap<String, Object>();
parts.add("from", email.getFrom());
parts.add("to", toCommaString(email.getTo()));
if (!email.getCc().isEmpty()) {
parts.add("cc", toCommaString(email.getCc()));
}
if (!email.getBcc().isEmpty()) {
parts.add("bcc", toCommaString(email.getBcc()));
}
parts.add("subject", email.getSubject());
if (email.getIsHtml()) {
parts.add("html", email.getBody());
} else {
parts.add("text", email.getBody());
}
email.getAttachments().forEach(attachment -> {
var decoded = Base64.getDecoder().decode(attachment.getBytes(StandardCharsets.UTF_8));
parts.add("attachment", new ByteArrayResource(decoded));
});
var header = headerProvider.createHeader("api", properties.getApiKey(), inferMediaType(email));
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(parts, header);
try {
var response = restTemplate.exchange(createDomain(properties.getDomain()), HttpMethod.POST, request, MailGunApiResponse.class);
log.info("Got the following MailGun response {}", response);
if (!response.getStatusCode().is2xxSuccessful()) {
email.setFailureReason(Optional.ofNullable(response.getBody()).map(MailGunApiResponse::getMessage).orElse(null));
email.setRetries(email.getRetries() + 1);
email.setFailed(isFailed(email));
} else {
email.setSent(true);
}
} catch (Exception e) {
log.error("An error has occurred while attempting to send out email {}", email, e);
email.setFailureReason(e.getMessage());
email.setRetries(email.getRetries() + 1);
email.setFailed(isFailed(email));
}
return email;
}
Does anyone know how to specify a filename for the attachment?

Download file from rest webservices spring

I'm trying to download any file calling my rest webservices. I'm using spring + jersey for the web services and Angular 2 for the front.
So when I clink on the front, the webservices get my file but the window to download it is not shown.
My rest API :
#POST
#Path("/download")
#ApiOperation(value = "Download")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadFile(#ApiParam(value = "File", required = true) String filePath) {
File file = new File("/webapps/pdf/upload/msg/1/gest.PNG");
Response.ResponseBuilder response = Response.ok((Object) file);
try {
String contentType = Files.probeContentType(file.toPath());
response.header("Content-Disposition", "attachment; filename="+file.getName());
response.header("Content-Type", contentType);
return response.build();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
My Angular service :
downloadFile(path) {
const headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded', 'Accept': '*'});
const options = new RequestOptions({headers: headers});
options.responseType = ResponseContentType.Blob;
return this.http.post(apiUrl + "msg/download", path, options)
.catch(this.handleError);
}
My Angular component :
downloadFile(documentPath) {
this.msgService.downloadFile(documentPath).subscribe(response => {
var contentType = response.headers('Content-Type');
let url = window.URL.createObjectURL(new Blob([response._body], {type: contentType}));
window.open(url);
});
}
Html :
<figure class="ui-g-12 " *ngFor="let document of msg.documents_path" (click)="downloadFile(document)">
<img [src]="selectImageByExtension(document.split('.').pop().toLowerCase())" />
<figcaption>{{document.split('/').pop().toLowerCase()}}</figcaption>
</figure>
When I click on my figure I can see that the file is well gotten:
But nothing pops up.
What did I miss ?
So the only solution working for me was to use GET request instead of POST passing the filepath as a pathparam.
Rest API :
#GET
#Path("/download/{filePath}")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getdownloadFile(#PathParam("filePath") String filePath) {
String path = null;
byte [] barr = Base64.getDecoder().decode(filePath);
path = new String(barr);
File file = new File(path);
try {
String contentType = Files.probeContentType(file.toPath());
Response.ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename="+file.getName());
response.header("Content-Type", contentType);
response.header("Content-Length", file.length());
return response.build();
} catch (IOException e) {
e.printStackTrace();
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
}
Angular service :
downloadFile(path) {
const headers = new Headers({'Content-Type': 'text/plain', 'Accept': '*'});
const options = new RequestOptions({headers: headers});
options.responseType = ResponseContentType.Blob;
return this.http.get(apiUrl + "msg/download/"+btoa(path), options)
.map(res => res)
.catch(this.handleError);
}
Angular component :
downloadFile(documentPath) {
this.msgService.downloadFile(documentPath).subscribe(response => {
let params = documentPath.split('/' );
var blob = new Blob([response._body]);
FileSaver.saveAs(blob, params[params.length-1]);
});
}

Downloading a file using AJAX GET from Spring Service

I'm trying to implement a Service that automatically starts a download with the requested file.
This is my AJAX call:
function downloadFile(fileName) {
$.ajax({
url : SERVICE_URI + "files/" + fileName,
contentType : 'application/json',
type : 'GET',
success : function (data)
{
alert("done!");
},
error: function (error) {
console.log(error);
}
});
}
and this is my Spring Service method GET:
#RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
public void getFile(#PathVariable("file_name") String fileName,
HttpServletResponse response) {
try {
// get your file as InputStream
FileInputStream fis = new FileInputStream( fileName + ".csv" );
InputStream is = fis;
// copy it to response's OutputStream
ByteStreams.copy(is, response.getOutputStream());
response.setContentType("text/csv");
response.flushBuffer();
} catch (IOException ex) {
throw new RuntimeException("IOError writing file to output stream");
}
}
When my client requests the existing file from the server, the AJAX success() method is executed but the file is not even downloading. Am I doing anything wrong?
Don't use ajax, just set window.location.href to the url of the file and set the http content disposition header in your server script to force the browser to save the file.
function downloadFile(fileName) {
window.location.href = SERVICE_URI + "files/" + fileName;
}

How to send file back to client browser from Java after jQuery.post()

I need help with sending xlsx-file from the server back to the client
This is how it worked BEFORE:
JavaScript (click #export_xls button):
export_xls: function(event) {
window.location = ... + this.workspace.query.id + "/export/xls";
}
Java (create xls-file using Apache POI API):
#GET
#Produces({"application/vnd.ms-excel" })
#Path("/{queryname}/export/xls/{format}")
public Response getQueryExcelExport(
#PathParam("queryname") String queryName,
#PathParam("format") #DefaultValue("flattened") String format){
// ...
try {
byte[] doc = olapQueryService.getExport(queryName,"xls","flat"); // file
String name = "file.xls";
return Response.ok(doc, MediaType.APPLICATION_OCTET_STREAM).header(
"content-disposition",
"attachment; filename = " + name).header(
"content-length",doc.length).build();
}
catch (Exception e) {
log.error("Cannot get excel for query (" + queryName + ")",e);
return Response.serverError().build();
}
}
And it worked fine, but now i need to send some data from javascript to the java, then java process it and create xlsx
So, i use ajax to send that data (in json format)...
export_xls: function(event) {
var data = this.workspace.query.result.lastresult();
var url = ... + this.workspace.query.id + "/testexportxls";
$.ajax({
url: url,
type: "POST",
data: JSON.stringify(data),
async: false,
contentType: "application/json"
});
},
...and create my file in java (almost like it was before):
#POST
#Produces({"application/vnd.ms-excel" })
#Consumes(MediaType.APPLICATION_JSON)
#Path("/{queryname}/testexportxls")
public Response setQueryExcelExport(final Object jsonData)
{
Workbook wb = MyFileBuilder.getFile(jsonData);
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
wb.write(bout);
byte[] doc = bout.toByteArray();
String name = "file.xlsx";
return Response.ok(doc, MediaType.APPLICATION_OCTET_STREAM).header(
"content-disposition",
"attachment; filename = " + name).header(
"content-length",doc.length).build();
}
catch (Exception e){
log.error("Error while xlsx-file creating. Exception message: ",e);
return Response.serverError().build();
}
}
But i can't get that file now, because of the ajax, i think.
Do you know some quick solution, with minimum code edits?
Unfortunately, I almost know nothing about Response, or some HttpServletResponse and stuff like that =/
Thank you for your time.
If you were to define a callback function on success, wouldn't that function be able to handle the file?
Seems not, so I'll have a second try: put the JSON in a hidden form input, and POST the form?

jQuery.ajax servlet return a file [duplicate]

This question already has an answer here:
How to download file from httpServlet with Jquery?
(1 answer)
Closed 5 years ago.
I'm pretty new to jQuery and ajax and i have a question.
In a jsp I call
function downloadDocument(documentId){
var action = "attachment.do";
var method = "downloadDocument";
var url = action + "?actionType="+method+"&documentId=" + documentId;
$.ajax({
url: url,
dataType: "json",
type: 'POST',
success: function(data){
alert("downloaded!");
},
error: function (request, status, error) {
alert(request.responseText);
}
});
then in the servlet I do
public void downloadDocument(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws IOException{
AttachmentActionForm form = (AttachmentActionForm)actionForm;
ServletOutputStream out = response.getOutputStream();
try{
// Get the downloadFileName, the name of the file when it will be downloaded by user
String downloadFileName = "hello.txt";
String mimetype = "application/x-download"
// Get the byte stream of the file
FileInputStream in = new FileInputStream(Global.ATTACHMENTS_SHARED_FOLDER_PATH + downloadFileName);
// Print out the byte stream
byte[] buffer = new byte[4096];
int length;
while ((length = in.read(buffer)) > 0){
out.write(buffer, 0, length);
}
response.addHeader("Content-Disposition", "attachment; filename="+downloadFileName);
response.setHeader("Content-Length", Integer.toString(length));
response.setContentType(mimetype);
in.close();
}
catch(Exception e){
response.setContentType("text/text;charset=utf-8");
response.setHeader("cache-control", "no-cache");
System.out.println(e.getMessage());
out.println(e.getMessage());
}finally{
out.flush();
}
}
But in the ajax function, I never get a success, all the time I get the error message, even if the message is composed by the string inside of the file. What can I do?
Remove your dataType: "json", options and you will see some debug informations.
By the way, there is a jQuery option that meet you need:
$.fileDownload('some/file.pdf')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); })
Taken from this answer: https://stackoverflow.com/a/9970672/1420186
EDIT:
Your JSP
function downloadDocument(documentId){
var action = "attachment.do";
var method = "downloadDocument";
var url = action + "?actionType="+method+"&documentId=" + documentId;
$.ajax({
url: url,
dataType: "text", // Change dataType to "text"
type: 'POST',
success: function(data){
if (data == "FAIL") {
alert("File not found!");
} else {
window.location.href = data; // Download the file
}
},
error: function (request, status, error) {
alert("The request failed: " + request.responseText);
}
});
}
In your Servlet, if the file is not exists, just return a "FAIL" string, else return the file URL.
Hope that helps.
dont use Ajax call use //use hidden form approach
<form action='../servletname' method='POST' id='formid'>
<input type='hidden' value='' name='name' id='id'/>
<input type='hidden' value=' ' name='name' id='id' />
</form>
on click of button submit form
$('#formid').submit();
in servlet
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=filnemae.fileformat");
ServletOutputStream out = res.getOutputStream();
write on ouput stream then close or flush
if you are sending large data through post update postsize in server.xml

Categories