I am trying to fully implement a resumable file upload system in Java. The library I am using is resumable.js which sends chunks of a file to save as .part and then merge them together at the end. When I receive the POST request, in my doPost method I take the request, save it into a HttpServletRequestWrapper and then use that to get all the data I need. However, when saving the files as .part I end up with them being empty and have a size of 0 bytes.
I have checked and it seems that the data is all there, but I can't seem to get the data to save. Is there something that I implemented incorrectly?
Here is a small snippet of the code I use to do this task:
public void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
HttpServletRequestWrapper wrapReq = new HttpServletRequestWrapper(request);
BufferedReader reader = wrapReq.getReader();
/**
* Get some data from the BufferedReader
*/
if(ServletFileUpload.isMultipartContent(wrapReq)){
File mkd = new File(temp_dir);
if(!mkd.isDirectory())
mkd.mkdirs();
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
Iterator<FileItem> iter = upload.parseRequest(request).iterator();
OutputStream out;
out = new FileOutputStream(new File(dest_dir));
while(iter.hasNext()){
try {
FileItem item = iter.next();
IOUtils.copy(item.getInputStream(), out);
logger.debug("Wrote file " + resumableIdentifier + " with chunk number "
+ resumableChunkNumber + " to " + temp_dir);
out.close();
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
}
}
tryCreateFileFromChunks(temp_dir, resumableFileName, resumableChunkSize, resumableTotalSize);
} catch (Exception e) {
e.printStackTrace();
}
}
Where the tryCreateFileFromChunks() method just checks if all the parts are there and merges them. It isn't the problem. The .part files themselves are being stored empty.
So, did I handle this the wrong way? I've been struggling to get this working correctly.
You shouldn't be using HttpServletRequestWrapper, nor be calling its getReader(). The request body can be read only once and you've to choose whether to use getReader() method, or getInputStream() method, or getParameterXxx() methods on the very same request and not mix them.
Apache Commons FileUpload uses internally getInputStream() to parse the request body. But if you've called getReader() or getParameterXxx() beforehand, then Apache Commons FileUpload will get an empty request body.
All with all, to fix your problem, just get rid of wrapReq altogether.
if(ServletFileUpload.isMultipartContent(request)){
// ...
See also:
How to upload files to server using JSP/Servlet?
Related
I followed the discussion on spark github page as well as stack overflow to understand how to upload files using spark and apache file uploads.
Now I want the user to have an option to download the image on click.
For example my uploaded files get stored in /tmp/imageName.jpg on the server.
On the client side i want to give the user an option to download the file when the user clicks in the hyperlink.
click here
When the user click on the hyperlink I will call the function with the file path but can't understand how to send the image in response.
I do know that HTML5 has download attribute but that would require the files to be kept in public folder on the server which is not possible.
I went through the previous similar question add tried to replicate for my scenario without success
How can I send a PNG of a QR-code in a HTTP response body (with Spark)?
How download file using java spark?
Edit:
I did follow the link provided in the answer to force download the image, but using response.raw() i'm not able to get the response
response.type("application/force-download");
response.header("Content-Transfer-Encoding", "binary");
response.header("Content-Disposition","attachment; filename=\"" + "xxx\"");//fileName);
try {
HttpServletResponse raw = response.raw();
PrintWriter out = raw.getWriter();
File f= new File("/tmp/Tulips.jpg");
InputStream in = new FileInputStream(f);
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
while(din.available() > 0){
out.print(din.read());
out.print("\n");
}
}
catch (Exception e1) {
e1.printStackTrace();
}
response.status(200);
return response.raw();
Edit 2:
I'm not sure what is the difference between using response.body () vs response.raw().someFunction(). In either case I can seem to send the data back in response. Even if i write a simple response.body("hello") it doesn't reflect in my response.
Is there a difference in how a file would be read as opposed to an image ? Exampling using ImageIO class ?
Below is the solution that work for me:
Service.java
get(API_CONTEXT + "/result/download", (request, response) -> {
String key = request.queryParams("filepath");
Path path = Paths.get("/tmp/"+key);
byte[] data = null;
try {
data = Files.readAllBytes(path);
} catch (Exception e1) {
e1.printStackTrace();
}
HttpServletResponse raw = response.raw();
response.header("Content-Disposition", "attachment; filename=image.jpg");
response.type("application/force-download");
try {
raw.getOutputStream().write(data);
raw.getOutputStream().flush();
raw.getOutputStream().close();
} catch (Exception e) {
e.printStackTrace();
}
return raw;
});
Angular Code
$scope.downloadImage= function(filepath) {
console.log(filepath);
window.open('/api/v1/result/download?filepath='+filepath,'_self','');
}
I am working on Java ExtJS application in which I need to create and download a CSV file.
On clicking a button I want a CSV file to be downloaded to a client's
machine.
On buttons listener I am calling a servlet using AJAX. There I am
creating a CSV file.
I don't want the CSV file to be saved in the server. I want the file should be created dynamically with a download option. I want the contents of a file to be created as a string and then I will serve the content as file in which it will open as download mode in browser (this I have achieved in other language, but not sure how to achieve it in Java).
Here is my code only to create a CSV file, but I really don't want to create or save CSV file if I can only download the file as CSV.
public String createCSV() {
try {
String filename = "c:\\test.csv";
FileWriter fw = new FileWriter(filename);
fw.append("XXXX");
fw.append(',');
fw.append("YYYY");
fw.append(',');
fw.append("ZZZZ");
fw.append(',');
fw.append("AAAA");
fw.append(',');
fw.append("BBBB");
fw.append('\n');
CSVResult.close();
return "Csv file Successfully created";
} catch(Exception e) {
return e.toString();
}
}
Can any one help me on this.
Thanks
I got the solution and I am posting it below.
public void doGet(HttpServletRequest request, HttpServletResponse response)
{
response.setContentType("text/csv");
response.setHeader("Content-Disposition", "attachment; filename=\"userDirectory.csv\"");
try
{
OutputStream outputStream = response.getOutputStream();
String outputResult = "xxxx, yyyy, zzzz, aaaa, bbbb, ccccc, dddd, eeee, ffff, gggg\n";
outputStream.write(outputResult.getBytes());
outputStream.flush();
outputStream.close();
}
catch(Exception e)
{
System.out.println(e.toString());
}
}
Here we don't need to save / store the file in the server.
Thanks
First of all you need to get the HttpServletResponse object so that you can stream a file into it.
Note : This example is something I Wrote for one of my projects and it works.Works on Java 7.
Assuming you got the HttpServletResponse you can do something like this to stream a file. This way the file will be saved into clients' machine.
public void downloadFile(HttpServletResponse response){
String sourceFile = "c:\\source.csv";
try {
FileInputStream inputStream = new FileInputStream(sourceFile);
String disposition = "attachment; fileName=outputfile.csv";
response.setContentType("text/csv");
response.setHeader("Content-Disposition", disposition);
response.setHeader("content-Length", String.valueOf(stream(inputStream, response.getOutputStream())));
} catch (IOException e) {
logger.error("Error occurred while downloading file {}",e);
}
}
And the stream method should be like this.
private long stream(InputStream input, OutputStream output) throws IOException {
try (ReadableByteChannel inputChannel = Channels.newChannel(input); WritableByteChannel outputChannel = Channels.newChannel(output)) {
ByteBuffer buffer = ByteBuffer.allocate(10240);
long size = 0;
while (inputChannel.read(buffer) != -1) {
buffer.flip();
size += outputChannel.write(buffer);
buffer.clear();
}
return size;
}
}
What this does is, get an inputstream from your source file and write that stream into the outputstream of the HttpServletResponse. This should work since it works perfectly for me. Hope this helps. Sorry for my bad English.
I would like add something to the answer by gaurav. I recently had to implment this functionality in a project of mine and using javascript was out of the question becuase we had to support IE 9. What is the problem with IE 9?
(Export to CSV using jQuery and html), see the second answer in the link.
I needed an easy way to convert a ResultSet of a database query to a string which represent the the same data in CSV format. For that I used http://opencsv.sourceforge.net/ which provided an easy way to get a String ot of the ResultSet, and the rest is as above answer did it.
THe examples in the project soruce folder give good examples.
I'm developing a web application on which user able to generate reports of some data. I'm using JasperReport do it. It's my first experience with JasperReport. So I've followed this tutorial.
Method described there can save reports on server, but I need that report would be downloadable after generation.
Could appreciate some help, thanks.
Your code should look something like this :
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
String path = getServletContext().getRealPath("/reports/report1.jrxml");
jasReport = JasperCompileManager.compileReport(path);
System.out.println("Jasper Report : " + jasReport);
Connection con = MyConnFactory.getConnection();
System.out.println(con);
jasPrint = JasperFillManager.fillReport(jasReport,null,con);//, mapParam, con);
System.out.println("Jasper Print : " + jasPrint);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// JasperExportManager.exportReportToPdfFile(jasPrint, "f:/nn.pdf");
// JasperExportManager.exportReportToPdfStream(jasPrint, baos);
// ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
// inputStream = bais;
ServletOutputStream sos=resp.getOutputStream();
JasperExportManager.exportReportToPdfStream(jasPrint, sos);
try {
MyConnFactory.getConnection().close();
sos.close();
} catch (SQLException ex) {
Logger.getLogger(MyReport.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (JRException ex) {
Logger.getLogger(MyReport.class.getName()).log(Level.SEVERE, null, ex);
}
}
You will need to set these in your servlet code when trying to download the generated file. May need to change some parameters depending on the file format for your specific case.
response.setContentType("APPLICATION/OCTET-STREAM");
String disHeader = "Attachment;Filename=\"ReportFile.csv" + "\"";
response.setHeader("Content-Disposition", disHeader);
Either:
Save the file to a location accessible directly by the client, or
Stream the bytes back from a file (or other byte stream).
To do #1, you'll either have to save it "inside" the web app, which isn't possible when deploying a war, and tenuous if deploying an exploded war. Symlinks and container games can work around that.
To do #2, save it anywhere, and create a servlet that can take a request parameter that maps to the generated file (or whatever) and streams it back--searching for "download servlet" gives a ton of implementations.
Most frameworks offer similar functionality without writing a pure servlet.
In this situation, I have created a zip file containing search result files, and am trying to send it to the user. Here is the chunk of code I am currently trying to use.
File[] zippable = new File[files.size()];
File resultFile = ZipCreator.zip(files.toArray(zippable), results);
InputStream result = new FileInputStream(resultFile);
IOUtils.copy(result, response.getOutputStream());
However, this currently doesn't work quite right. Instead of returning the zip file that I have created, it returns an html file. If I manually change the file extension afterwards, I can see that the contents of the file are still the search results that I need. So the problem just lies in returning the proper extension to the response.
Does anyone have any advice for this situation?
You need to set the Content-Type response header to the value application/zip (or application/octet-stream, depending on the target browser). Additionally, you may want to send additional response headers indicating attachment status and filename.
You need to set the content type header to application/octet-stream prior to streaming the results. Depends on what implementation of response you are using on how you actually do this.
Here is some working code, just in case anyone needs it:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// The zip file you want to download
File zipFile = new File(zipsResourcesPath + zipFileName);
response.setContentType("application/zip");
response.addHeader("Content-Disposition", "attachment; filename=" + zipFileName);
response.setContentLength((int) zipFile.length());
try {
FileInputStream fileInputStream = new FileInputStream(zipFile);
OutputStream responseOutputStream = response.getOutputStream();
int bytes;
while ((bytes = fileInputStream.read()) != -1) {
responseOutputStream.write(bytes);
}
} catch (IOException e) {
logger.error("Exception: " + e);
}
}
And the HTML:
<a class="btn" href="/path_to_servlet" target="_blank">Download zip</a>
Hope this helps!
So I found a hack for this : ) Just add ".zip" in your filename and set your content type as application/zip. Works like a charm.
response.setContentType("application/zip");
String licenseFileName = eId;
response.setHeader("Content-disposition", "attachment; filename=\"" + licenseFileName +".zip");
I have a servlet which is meant to handle the upload of a very large file. I am trying to use commons fileupload to handle it. Currently, the file I am attempting to upload is 287MB.
I set up the FileItemFactory and ServletFileUpload, then set a very large max file size on the ServletFileUpload.
Unfortunately, when I attempt to create a FileItemIterator, nothing happens. The form is set with the correct action, multipart encoding, and for the POST method.
Can anyone assist? doPost() of the servlet is posted below:
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ensure that the form is multipart encoded since we are uploading a file
if (!ServletFileUpload.isMultipartContent(req)) {
//throw new FileUploadException("Request was not multipart");
log.debug("Request was not multipart. Returning from call");
}
// create a list to hold all of the files
List<File> fileList = new ArrayList<File>();
try {
// setup the factories and file upload stuff
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(999999999);
// create a file item iterator to cycle through all of the files in the req. There SHOULD only be one, though
FileItemIterator iterator = upload.getItemIterator(req);
// iterate through the file items and create a file item stream to output the file
while (iterator.hasNext()) {
// get the file item stream from the iterator
FileItemStream fileItemStream = iterator.next();
// Use the Special InputStream type, passing it the stream and the length of the file
InputStream inputStream = new UploadProgressInputStream(fileItemStream.openStream(), req.getContentLength());
// create a File from the file name
String fileName = fileItemStream.getName(); // this only returns the filename, not the full path
File file = new File(tempDirectory, fileName);
// add the file to the list
fileList.add(file);
// Use commons-io Streams to copy from the inputstrea to a brand-new file
Streams.copy(inputStream, new FileOutputStream(file), true);
// close the inputstream
inputStream.close();
}
} catch (FileUploadException e) {
e.printStackTrace();
}
// now that we've save the file, we can process it.
if (fileList.size() == 0) {
log.debug("No File in the file list. returning.");
return;
}
for (File file : fileList) {
String fileName = file.getName();
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String line = reader.readLine();
List<Feature> featureList = new ArrayList<Feature>(); // arraylist may not be the best choice since I don't know how many features I'm importing
while (!line.isEmpty()) {
String[] splitLine = line.split("|");
Feature feature = new Feature();
feature.setId(Integer.parseInt(splitLine[0]));
feature.setName(splitLine[1]);
feature.setFeatureClass(splitLine[2]);
feature.setLat(Double.parseDouble(splitLine[9]));
feature.setLng(Double.parseDouble(splitLine[10]));
featureList.add(feature);
line = reader.readLine();
}
file.delete(); // todo: check this to ensure it won't blow up the code since we're iterating in a for each
reader.close(); // todo: need this in a finally block somewhere to ensure this always happens.
try {
featureService.persistList(featureList);
} catch (ServiceException e) {
log.debug("Caught Service Exception in FeatureUploadService.", e);
}
}
}
It was an incredibly stupid problem. I left the name attribute off of the FileUpload entry in the GWT UiBinder. Thanks for all of the help from everyone.
Are the only request parameters available File items? Because you may want to put in a check:
if (!fileItemStream.isFormField()){
// then process as file
otherwise you'll get errors. On the surface of things your code looks fine: no errors in the Tomcat logs?
You need to add enctype='multipart/form-data' in html form