Download .csv with Play! framework - csv

EDIT
From what I have learned (from comments by nico_ekito) it is not possible to use ajax call to download a file. The solution is to create a hidden <iframe> that will download the file, described here.
Problem:
The browser doesn't show download dialog. Any browser - ff, opera, chrome, safari, ie.
I read the docs about serving files, found this question and, based on this, wrote:
Controller.response().setContentType("text/csv");
Controller.response().setHeader("Content-Disposition", "attachment;filename=public/export/filename.csv");
Controller.response().setHeader("Cache-control", "private");
return ok(CSV.export(data, filename));
Where CSV is a class:
public class CSV{
public static File export(Map<String, String> data, String filename){
String csv = data.get("data[saveMe]");
try {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("public/export/" + filename), "UTF-8"));
bw.write(csv);
bw.newLine();
bw.flush();
bw.close();
}catch(UnsupportedEncodingException e){}
catch(FileNotFoundException e){}
catch(IOException e){}
File downloadMe = new File("public/export/" + filename);
return downloadMe;
}
}
On client side, I use dojo to send POST request (I also tried with GET, result is the same):
xhr.post({
url: '/exportData/',
content: {
saveMe: str
}
}).then(function(response){
//do sth
});
Response headers look like that:
Cache-Control private
Content-Disposition attachment;filename=public/export/filename.csv
Content-Type text/csv
Transfer-Encoding chunked
POST tab in firebug shows proper data in proper csv format. To format data with csv style I use dojox/grid/enhanced/plugins/exporter/CSVWriter

I think it is not possible to download a file from an Ajax request, see this question: Allow User to Download File using Ajax
You should either use an iframe as suggested in the question, or use a standard HTML form containing your data, and do a post on this form.

Related

Retrieving binary file from post request

Sending a POST request (Apache httpclient, here Kotlin source code):
val httpPost = HttpPost("http://localhost:8000")
val builder = MultipartEntityBuilder.create()
builder.addBinaryBody("file", File("testFile.zip"),
ContentType.APPLICATION_OCTET_STREAM, "file.ext")
val multipart = builder.build()
httpPost.entity = multipart
val r = httpClient.execute(httpPost)
r.close()
I receive the request in my post handler as a via spark-java Request-object. How do I retrieve the original file (plus the file name as a bonus) from the post request? The request.bodyAsBytes() method seems to add some bytes because the body is larger than the original file.
Thanks, Jörg
Near the bottom of Spark's Documentation page there is a section "Examples and FAQ". The first example is "How do I upload something?".
From there, it links further to an example on GitHub.
In short:
post("/yourUploadPath", (request, response) -> {
request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp"));
try (InputStream is = request.raw().getPart("file").getInputStream()) {
// Use the input stream to create a file
}
return "File uploaded";
});
To access the original file name:
request.raw().getPart("file").getSubmittedFileName()
To handle multiple files or parts, I usually have code similar to the following (assuming only files are included in the multi-part encoded upload):
for (Part part : req.raw().getParts()) {
try (InputStream stream = part.getInputStream()) {
String filename = part.getSubmittedFileName();
// save the input stream to the filesystem, and the filename to a database
}
}

File upload to Web API using Multipart request

I have a Silverlight application that uses Web API to upload a document that is stored in a Database as a Filestream. Currently it's done by a POST with a Content-Type: application/json. The object containing a byte array of the File along with some metadata about the file is serialized to JSON and posted to the Web API. Web API then saves the byte array as a Filestream to the Database.
Following is a sample of the current request:
{"FileContent":"JVBERi0xLjUNJeLjz9MNCjEwIDAgb2JqDTw8L0xpbmVhcml6ZWQgMS9MIDI3MTg2L08gMTIvRSAyMjYyNi9OIDEvVCAyNjg4NC9IIFsgNDg5IDE2OF0+Pg1lbmRvYmoNICAgICAgICAgICAgICAgICAgDQoyNyAwIG9iag08PC9EZWNvZGVQYXJtczw8L0NvbHVtbnMgNC9QcmVkaWN0b3IgMTIg0K","ProductId":"85c98324-092a-4d10-bab0-03912e437234","OrderId":"7b826322-7526-4a69-b67c-5c88a04f4c60","FileName":"test.pdf","FileType":1,"FileDescription":"test"}
I would like to change this logic to Post as a Content-Type of Multipart. What would be the best way to form my request? Also, what's the best way to structure my Web API Controller to process the Multipart request?
This is a sample for a Multipart upload.
[HttpPost]
[Route("upload")]
public async Task<IHttpActionResult> Upload()
{
MultipartFileData file = null;
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
return UnsupportedMediaType();
}
// initialize path and provider
string root = HttpContext.Current.Server.MapPath("~/App_Data");
if (Directory.Exists(root) == false) Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
try
{
// we take the first file here
file = provider.FileData[0];
// and the associated datas
int myInteger;
if (int.TryParse(provider.FormData["MyIntergerData"], out myInteger) == false)
throw new ArgumentException("myInteger is missing or not valid.");
var fileContent = File.ReadAllBytes(file.LocalFileName);
// do something with your file!
}
finally
{
// get rid of temporary file
if (file != null)
File.Delete(file.LocalFileName);
}
// successfull!
return NoContent();
}
This is a sample I got from an API of mine. You can have multiple files for each upload (check the provider.FileData array), and different datas inside the provider.FormData array.
For the client side aspect of this I suggest you to check this answer for a sample of a JS call to this API.
Hope it helps!

Phonegap/HTML - How to show a pdf returned by a webservice?

I've got a SOAP webservice that returns a binary stream of a .PDF file.
The file doesn't exists physically, I need to know if there is a way to make the browser interpret this stream like an HTML link to show generate and show the file: DOWNLOAD FILE
You can put a page in a web server that retrieves the PDF stream, sets the required parameters in the header of the response, and sends the response to the client.
Example using C# and asp.net:
The HTML link would be: DOWNLOAD FILE
The code in mypdf.aspx would be:
protected void Page_Load(object sender, EventArgs e)
{
Response.ClearContent();
Response.AddHeader("content-disposition",
"attachment;filename=FilledForm.pdf");
Response.ContentType = "application/pdf";
byte[] bytes = GetPDFBytesFromWebService();
Response.BinaryWrite(bytes);
Response.End();
}

IE9 JSON Data "do you want to open or save this file"

Started testing my jQuery applications with IE9. Looks like I may be in for some trouble here.
I noticed that when I return JSON data back to the Javascript methods I always get this Prompt that says: "Do you want to open or save this file?" and provides me with 3 buttons: Open, Save and Cancel. Of course, my javascript is taking actions based on the values set in the JSON object but since IE9 doesn't pass it over to the script, I cannot execute the follow up action from there on.
Anyone else facing this issue? Here is a snapshot.
If anyone is using ASP.net MVC and trying to fix this issue - I used the following built in methods in the MVC framework. Simply update the content Type and encoding on the JsonResult.
public ActionResult Index(int id)
{
// Fetch some data
var someData = GetSomeData();
// Return and update content type and encoding
return Json(someData, "text/html", System.Text.Encoding.UTF8,
JsonRequestBehavior.AllowGet);
}
This fixed the issue for me!
(Answer originally posted for this question.)
If using MVC, one way of handling this is to implement a base controller in which you override (hide) the Json(object) method as follows:
public class ExtendedController : Controller
{
protected new JsonResult Json(object data)
{
if (!Request.AcceptTypes.Contains("application/json"))
return base.Json(data, "text/plain");
else
return base.Json(data);
}
}
Now, your controllers can all inherit ExtendedController and simply call return Json(model); ...
without modifying the response content type for those browsers which play nicely (not <=IE9 !)
without having to remember to use Json(data, "text/plain") in your various Ajax action methods
This works with json requests which would otherwise display the "Open or Save" message in IE8 & IE9 such as those made by jQuery File Upload
I also faced this problem yesterday with WebAPI which returned a list of URLs (of asynchronously uploaded files).
Just set content type to "text/html" instead of default "application/json; charset=UTF-8" of WebAPI services. I got response as a JSON string and then used $.parseJSON to convert it to JSON object.
public async Task<HttpResponseMessage> Upload()
{
// ...
var response = Request.CreateResponse(HttpStatusCode.OK, files);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
// result is an iframe's body content that received response.
$.each($.parseJSON(result.html()), function (i, item)
{
console.log(item.Url);
});
In my case when contentType in response header is "application/json; charset=UTF-8", the IE 9 shows that Prompt. But changed to "text/html" then the prompt does not show, although all otter browsers are fine with the "application/json; charset=UTF-8".
Actually, you were right #EricLaw. After setting the content type in the Json result, it worked.
I had to add the following lines:
result.ContentEncoding = System.Text.Encoding.UTF8;
result.ContentType = "application/json; charset=UTF-8

Posting a File and Associated Data to a RESTful WebService preferably as JSON

In an application I am developing RESTful API and we want the client to send data as JSON. Part of this application requires the client to upload a file (usually an image) as well as information about the image.
I'm having a hard time tracking down how this happens in a single request. Is it possible to Base64 the file data into a JSON string? Am I going to need to perform 2 posts to the server? Should I not be using JSON for this?
As a side note, we're using Grails on the backend and these services are accessed by native mobile clients (iPhone, Android, etc), if any of that makes a difference.
I asked a similar question here:
How do I upload a file with metadata using a REST web service?
You basically have three choices:
Base64 encode the file, at the expense of increasing the data size by around 33%, and add processing overhead in both the server and the client for encoding/decoding.
Send the file first in a multipart/form-data POST, and return an ID to the client. The client then sends the metadata with the ID, and the server re-associates the file and the metadata.
Send the metadata first, and return an ID to the client. The client then sends the file with the ID, and the server re-associates the file and the metadata.
You can send the file and data over in one request using the multipart/form-data content type:
In many applications, it is possible for a user to be presented with
a form. The user will fill out the form, including information that
is typed, generated by user input, or included from files that the
user has selected. When the form is filled out, the data from the
form is sent from the user to the receiving application.
The definition of MultiPart/Form-Data is derived from one of those
applications...
From http://www.faqs.org/rfcs/rfc2388.html:
"multipart/form-data" contains a series of parts. Each part is
expected to contain a content-disposition header [RFC 2183] where the
disposition type is "form-data", and where the disposition contains
an (additional) parameter of "name", where the value of that
parameter is the original field name in the form. For example, a part
might contain a header:
Content-Disposition: form-data; name="user"
with the value corresponding to the entry of the "user" field.
You can include file information or field information within each section between boundaries. I've successfully implemented a RESTful service that required the user to submit both data and a form, and multipart/form-data worked perfectly. The service was built using Java/Spring, and the client was using C#, so unfortunately I don't have any Grails examples to give you concerning how to set up the service. You don't need to use JSON in this case since each "form-data" section provides you a place to specify the name of the parameter and its value.
The good thing about using multipart/form-data is that you're using HTTP-defined headers, so you're sticking with the REST philosophy of using existing HTTP tools to create your service.
I know that this thread is quite old, however, I am missing here one option. If you have metadata (in any format) that you want to send along with the data to upload, you can make a single multipart/related request.
The Multipart/Related media type is intended for compound objects consisting of several inter-related body parts.
You can check RFC 2387 specification for more in-depth details.
Basically each part of such a request can have content with different type and all parts are somehow related (e.g. an image and it metadata). The parts are identified by a boundary string, and the final boundary string is followed by two hyphens.
Example:
POST /upload HTTP/1.1
Host: www.hostname.com
Content-Type: multipart/related; boundary=xyz
Content-Length: [actual-content-length]
--xyz
Content-Type: application/json; charset=UTF-8
{
"name": "Sample image",
"desc": "...",
...
}
--xyz
Content-Type: image/jpeg
[image data]
[image data]
[image data]
...
--foo_bar_baz--
Here is my approach API (i use example) - as you can see, you I don't use any file_id (uploaded file identifier to the server) in API:
Create photo object on server:
POST: /projects/{project_id}/photos
body: { name: "some_schema.jpg", comment: "blah"}
response: photo_id
Upload file (note that file is in singular form because it is only one per photo):
POST: /projects/{project_id}/photos/{photo_id}/file
body: file to upload
response: -
And then for instance:
Read photos list
GET: /projects/{project_id}/photos
response: [ photo, photo, photo, ... ] (array of objects)
Read some photo details
GET: /projects/{project_id}/photos/{photo_id}
response: { id: 666, name: 'some_schema.jpg', comment:'blah'} (photo object)
Read photo file
GET: /projects/{project_id}/photos/{photo_id}/file
response: file content
So the conclusion is that, first you create an object (photo) by POST, and then you send second request with the file (again POST). To not have problems with CACHE in this approach we assume that we can only delete old photos and add new - no update binary photo files (because new binary file is in fact... NEW photo). However if you need to be able to update binary files and cache them, then in point 4 return also fileId and change 5 to GET: /projects/{project_id}/photos/{photo_id}/files/{fileId}.
I know this question is old, but in the last days I had searched whole web to solution this same question. I have grails REST webservices and iPhone Client that send pictures, title and description.
I don't know if my approach is the best, but is so easy and simple.
I take a picture using the UIImagePickerController and send to server the NSData using the header tags of request to send the picture's data.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"myServerAddress"]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:UIImageJPEGRepresentation(picture, 0.5)];
[request setValue:#"image/jpeg" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"myPhotoTitle" forHTTPHeaderField:#"Photo-Title"];
[request setValue:#"myPhotoDescription" forHTTPHeaderField:#"Photo-Description"];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
At the server side, I receive the photo using the code:
InputStream is = request.inputStream
def receivedPhotoFile = (IOUtils.toByteArray(is))
def photo = new Photo()
photo.photoFile = receivedPhotoFile //photoFile is a transient attribute
photo.title = request.getHeader("Photo-Title")
photo.description = request.getHeader("Photo-Description")
photo.imageURL = "temp"
if (photo.save()) {
File saveLocation = grailsAttributes.getApplicationContext().getResource(File.separator + "images").getFile()
saveLocation.mkdirs()
File tempFile = File.createTempFile("photo", ".jpg", saveLocation)
photo.imageURL = saveLocation.getName() + "/" + tempFile.getName()
tempFile.append(photo.photoFile);
} else {
println("Error")
}
I don't know if I have problems in future, but now is working fine in production environment.
FormData Objects: Upload Files Using Ajax
XMLHttpRequest Level 2 adds support for the new FormData interface.
FormData objects provide a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest send() method.
function AjaxFileUpload() {
var file = document.getElementById("files");
//var file = fileInput;
var fd = new FormData();
fd.append("imageFileData", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", '/ws/fileUpload.do');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
alert('success');
}
else if (uploadResult == 'success')
alert('error');
};
xhr.send(fd);
}
https://developer.mozilla.org/en-US/docs/Web/API/FormData
Since the only missing example is the ANDROID example, I'll add it.
This technique uses a custom AsyncTask that should be declared inside your Activity class.
private class UploadFile extends AsyncTask<Void, Integer, String> {
#Override
protected void onPreExecute() {
// set a status bar or show a dialog to the user here
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... progress) {
// progress[0] is the current status (e.g. 10%)
// here you can update the user interface with the current status
}
#Override
protected String doInBackground(Void... params) {
return uploadFile();
}
private String uploadFile() {
String responseString = null;
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://example.com/upload-file");
try {
AndroidMultiPartEntity ampEntity = new AndroidMultiPartEntity(
new ProgressListener() {
#Override
public void transferred(long num) {
// this trigger the progressUpdate event
publishProgress((int) ((num / (float) totalSize) * 100));
}
});
File myFile = new File("/my/image/path/example.jpg");
ampEntity.addPart("fileFieldName", new FileBody(myFile));
totalSize = ampEntity.getContentLength();
httpPost.setEntity(ampEntity);
// Making server call
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode == 200) {
responseString = EntityUtils.toString(httpEntity);
} else {
responseString = "Error, http status: "
+ statusCode;
}
} catch (Exception e) {
responseString = e.getMessage();
}
return responseString;
}
#Override
protected void onPostExecute(String result) {
// if you want update the user interface with upload result
super.onPostExecute(result);
}
}
So, when you want to upload your file just call:
new UploadFile().execute();
I wanted send some strings to backend server. I didnt use json with multipart, I have used request params.
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public void uploadFile(HttpServletRequest request,
HttpServletResponse response, #RequestParam("uuid") String uuid,
#RequestParam("type") DocType type,
#RequestParam("file") MultipartFile uploadfile)
Url would look like
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
I am passing two params (uuid and type) along with file upload.
Hope this will help who don't have the complex json data to send.
You could try using https://square.github.io/okhttp/ library.
You can set the request body to multipart and then add the file and json objects separately like so:
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("uploadFile", uploadFile.getName(), okhttp3.RequestBody.create(uploadFile, MediaType.parse("image/png")))
.addFormDataPart("file metadata", json)
.build();
Request request = new Request.Builder()
.url("https://uploadurl.com/uploadFile")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
logger.info(response.body().string());
#RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST)
public #ResponseBody Object jsongStrImage(#RequestParam(value="image") MultipartFile image, #RequestParam String jsonStr) {
-- use com.fasterxml.jackson.databind.ObjectMapper convert Json String to Object
}
Please ensure that you have following import. Ofcourse other standard imports
import org.springframework.core.io.FileSystemResource
void uploadzipFiles(String token) {
RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000)
def zipFile = new File("testdata.zip")
def Id = "001G00000"
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>()
form.add("id", id)
form.add('file',new FileSystemResource(zipFile))
def urld ='''http://URL''';
def resp = rest.post(urld) {
header('X-Auth-Token', clientSecret)
contentType "multipart/form-data"
body(form)
}
println "resp::"+resp
println "resp::"+resp.text
println "resp::"+resp.headers
println "resp::"+resp.body
println "resp::"+resp.status
}