I am trying to create a method that allows users to download a file on the file system via a Servlet. Right now, the correct filename shows on the download and a file is downloaded but the contents of the file are always null.
I've also tried to get the file contents to print to the console but nothing is showing up.
Can anyone suggest what I've done wrong here?
Thanks
String filePath = "uploads/" + request.getParameter( "filename" );
File downloadFile = new File( filePath );
String relativePath = getServletContext().getRealPath( "" );
System.out.println( "relativePath = " + relativePath );
ServletContext context = getServletContext();
String mimeType = context.getMimeType( filePath );
if( mimeType == null )
{
mimeType = "application/octet-stream";
}
System.out.println( "MIME type: " + mimeType );
response.setContentType( mimeType );
response.setContentLength( (int) downloadFile.length() );
String headerKey = "Content-Disposition";
String headerValue = String.format( "attachment; filename=\"%s\"", downloadFile.getName() );
response.setHeader( headerKey, headerValue );
byte[] bytes = Files.readAllBytes( downloadFile.getAbsoluteFile().toPath() );
OutputStream outStream = response.getOutputStream();
System.out.println( bytes.toString() );
outStream.write( bytes );
outStream.flush();
outStream.close();
Annotations
#WebServlet(urlPatterns =
{ "/Routing/*" })
#MultipartConfig(location = "/tmp", fileSizeThreshold = 1024 * 1024, maxFileSize = 1024 * 1024 * 5, maxRequestSize = 1024 * 1024 * 5 * 5)
public class Routing extends HttpServlet
{
This solution is a modified solution from BalusC File Servlet blog.
The reason I use this solution is because it reset() the HttpServletResponse response before writing data.
#WebServlet(urlPatterns =
{ "/Routing/*" })
#MultipartConfig(location = "/tmp", fileSizeThreshold = 1024 * 1024, maxFileSize = 1024 * 1024 * 5, maxRequestSize = 1024 * 1024 * 5 * 5)
public class FileServlet extends HttpServlet {
// Constants ----------------------------------------------------------------------------------
private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.
// Actions ------------------------------------------------------------------------------------
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// Get requested file by path info.
String filePath = "uploads/" + request.getParameter( "filename" );
// Check if file is actually supplied to the request URI.
if (filePath == null) {
// Do your thing if the file is not supplied to the request URI.
// Throw an exception, or send 404, or show default/warning page, or just ignore it.
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
return;
}
// Decode the file name (might contain spaces and on) and prepare file object.
File downloadFile = new File( filePath );
// Check if file actually exists in filesystem.
if (!downloadFile.exists()) {
// Do your thing if the file appears to be non-existing.
// Throw an exception, or send 404, or show default/warning page, or just ignore it.
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
return;
}
// Get content type by filename.
String contentType = getServletContext().getMimeType(file.getName());
// If content type is unknown, then set the default value.
// For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
// To add new content types, add new mime-mapping entry in web.xml.
if (contentType == null) {
contentType = "application/octet-stream";
}
// Init servlet response.
response.reset();
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setContentType(contentType);
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
// Prepare streams.
BufferedInputStream input = null;
BufferedOutputStream output = null;
try {
// Open streams.
input = new BufferedInputStream(new FileInputStream(file), DEFAULT_BUFFER_SIZE);
output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
// Write file contents to response.
byte[] bytes = Files.readAllBytes( downloadFile.getAbsoluteFile().toPath() );
output.write(buffer, 0, bytes.length);
} finally {
// Gently close streams.
close(output);
close(input);
}
}
// Helpers (can be refactored to public utility class) ----------------------------------------
private static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
// Do your thing with the exception. Print it, log it or mail it.
e.printStackTrace();
}
}
}
}
I hope this helps.
I recommend several tests to isolate the problem since there are still too many unknowns here. I had no problems running your code, so it is likely a configuration issue with your servlet container, or an assumption about the filesystem.
The key to isolating the problem is start with the basics. Try to return a String instead of a file, to ensure that the communication with the server is actually behaving properly. If you get the same response, then you know the problem isn't with the file IO:
byte[] bytes = "This is a test.".getBytes();
int contentLength = bytes.length;
String mimeType = "text/plain";
response.setContentType( mimeType );
response.setContentLength( contentLength );
OutputStream outStream = response.getOutputStream();
outStream.write( bytes );
outStream.flush();
outStream.close();
If the above test worked, then you know the problem is with the file or the method you are using to read the file. You code currently makes a lot of assumptions about everything being perfect with the file that is being requested. Perform some rigorous testing to make certain the file is accessible:
int fileSize = downloadFile.length();
System.out.println("File path: " + downloadFile.getAbsolutePath());
System.out.println("exists: " + downloadFile.exists());
System.out.println("canRead: " + downloadFile.canRead());
System.out.println("File size: " + fileSize);
and finally check the file size reported by the filesystem against the number of bytes that were read:
byte[] bytes = Files.readAllBytes( downloadFile.getAbsoluteFile().toPath() );
int bytesRead = bytes.length;
System.out.println("bytes read: " + bytesRead);
The results of these tests should narrow down the problem for you.
maybe the file is empty add this line to make sure the file is not empty
System.out.println("File size: " + bytes.length);
Just add
outStream.setContentLength(inStream.available);
after outStream.write( bytes );
Related
I need to generate a bunch of xml files with data from my java based web system, that represent a whole export of another system based in XML. Such system will accept this import later.
My approach is to create all files in memory, then save each one as entries it to a zip also in memory, which later is served to the client.
The data flow is working perfectly, but somehow the output is a blank file. I think I got wrong the outputstream structure
This is the part that I might be getting wrong:
...
//ZIP creation in server memory
ByteArrayOutputStream datosEnMemoria = new ByteArrayOutputStream();
ZipOutputStream zipped_out = new ZipOutputStream(datosEnMemoria)
...
//close and zip entry
xmlData.append(Tangonet.terminarCargaRegistros());
byte[] xmlBinData = xmlData.toString().getBytes();
zipped_out.write(xmlBinData, 0, xmlBinData.length);
zipped_out.closeEntry();
}
byte[] zipped_out_size = zipped_out.toString().getBytes();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=cierresZ_a_tangonet" + java.time.LocalDate.now() + ".zip");
response.setHeader("Content-length", "" + zipped_out_size.length);
response.setHeader("Content-Type", "application/zip");
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Transfer-Encoding", " binary");
//closing zip and send it to client
zipped_out.flush();
zipped_out.close();
// out.flush();
// out.close();
This is the full code:
#RequestMapping(value = "/cierreZ/exportar", method = RequestMethod.GET)
public void cierreZExportar(#ModelAttribute InformesFinancierosForm informesFinancierosForm, HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpSession session = request.getSession(true);
String fechaInicio = null;
String fechaFin = null;
if (session.getAttribute("mesActual") != null) {
informesFinancierosForm.setFechaInicio("01-" + informesFinancierosForm.getMes());
informesFinancierosForm.setFechaFin(new SimpleDateFormat("dd-MM-yyyy").format(DateUtil.getUltimoDiaDelMes(DateUtil.traduceDateDate((String) session.getAttribute("fechaIni")))));
fechaInicio = informesFinancierosForm.getFechaInicio();
fechaFin = informesFinancierosForm.getFechaFin();
} else {
fechaInicio = (String) session.getAttribute("fechaIni");
fechaFin = (String) session.getAttribute("fechaFin");
}
if (informeService.isRangoFechaValido(informesFinancierosForm.getSalasSeleccionadas(), fechaInicio)) {
if (!(fechaInicio.compareTo("") == 0) || (fechaFin.compareTo("") == 0)
|| informesFinancierosForm.getSalasSeleccionadas().length == 0) {
// ServletOutputStream out = response.getOutputStream();
List<InformeCierreZItemForm> listadoInfCierreZ = cierreZService.getCierres(informesFinancierosForm.getSalasSeleccionadas(), fechaInicio, fechaFin);
//ZIP creation in server memory
ByteArrayOutputStream datosEnMemoria = new ByteArrayOutputStream();
ZipOutputStream zipped_out = new ZipOutputStream(datosEnMemoria);
//filling zip with static xml files
for (int i = 0; i < Tangonet.documentos_estaticos_tangonet.length; i++) {
ZipEntry xmlFile = new ZipEntry(Tangonet.documentos_estaticos_tangonet[i][0] + ".xml");
zipped_out.putNextEntry(xmlFile);
StringBuilder xmlData = new StringBuilder();
xmlData.append(Tangonet.documentos_estaticos_tangonet[i][1]);
byte[] xmlBinData = xmlData.toString().getBytes();
zipped_out.write(xmlBinData, 0, xmlBinData.length);
zipped_out.closeEntry();
}
//filling zip with dynamic xml files
for (int i = 0; i < Tangonet.documentos_dinamicos_tangonet.length; i++) {
//dynamic xml creation
ZipEntry xmlFile = new ZipEntry(Tangonet.documentos_dinamicos_tangonet[i][0] + ".xml");
zipped_out.putNextEntry(xmlFile);
//xml schema
StringBuilder xmlData = new StringBuilder();
xmlData.append(Tangonet.documentos_dinamicos_tangonet[i][1]);
//xml data rows
for (InformeCierreZItemForm informeCierreZActual : listadoInfCierreZ) {
Sala salaActual = informeCierreZActual.getSala();
CierrezList CierresZ = cierreZService.getCierresZ(salaActual, fechaInicio, fechaFin);
//fiscal data in rows
Tangonet datosFiscalesCierrezActual = tangonetDatos.getDatosFiscales(salaActual);
for (Cierrez cierreActual : CierresZ) {
if (Tangonet.documentos_dinamicos_tangonet[i][0].equals("Comp_de_FacturaciĆ³n_para_Cobranza_Centralizada___GVA12")) {
xmlData.append(datosFiscalesCierrezActual.crearRegistroGVA12(cierreActual));
} else {
xmlData.append(datosFiscalesCierrezActual.crearRegistroGVA42(cierreActual));
}
}
}
//close and zip entry
xmlData.append(Tangonet.terminarCargaRegistros());
byte[] xmlBinData = xmlData.toString().getBytes();
zipped_out.write(xmlBinData, 0, xmlBinData.length);
zipped_out.closeEntry();
}
byte[] zipped_out_size = zipped_out.toString().getBytes();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=cierresZ_a_tangonet" + java.time.LocalDate.now() + ".zip");
response.setHeader("Content-length", "" + zipped_out_size.length);
response.setHeader("Content-Type", "application/zip");
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Transfer-Encoding", " binary");
//closing zip and send it to client
zipped_out.flush();
zipped_out.close();
// out.flush();
// out.close();
}
}
}
Zip file can be big, so don't generate it in memory. Write it straight to client.
Also:
Don't set Content-Type three times. It can only have one value.
Don't specify Content-Transfer-Encoding. It's an email header, not an HTTP header.
Since you'll be streaming, don't specify Content-length.
// headers must be set before streaming
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=cierresZ_a_tangonet" + java.time.LocalDate.now() + ".zip");
// stream straight to client
ZipOutputStream zipped_out = new ZipOutputStream(response.getOutputStream());
// Add zip entries and data here:
// Loop:
// zipped_out.putNextEntry(...)
// Generate XML, writing it straight to zipped_out
// Remember to flush any streams/writers wrapped around zipped_out
// Do not close zipped_out or wrappers of it
// If that cannot be prevented, use a CloseShieldOutputStream (from Commons IO)
// No need to call zipped_out.closeEntry()
// make sure to finish the zip stream
zipped_out.finish();
I have written the following code to download files from the file server. While downloading, I do not get any runtime error but it always says file corrupt.
I am retrieving the file details from the database:
Reading the contents in byte form to the output stream.
This code works fine for all txt files however for all other types, the size of file increases and when i try to open, it says file is corrupted
public void download(Long requestId, HttpServletResponse response) throws Exception {
byte[] out;
ServletOutputStream outputStream;
String fileServerPath = parametersService.getParameters(AppConstants.FILE_SERVER_PATH);
FileVO fileVO = dao.getFileDetails(requestId);
String filePath = fileServerPath + fileVO.getFilePath() + fileVO.getFileName();
try {
if (FileUtil.isFileExist(filePath)) {
File apFile = new File(filePath);
InputStream in = new BufferedInputStream(new FileInputStream(apFile));
String mimeType= URLConnection.guessContentTypeFromName(apFile.getName());
if(mimeType==null){
//if mimetype is not detectable, will take default
mimeType = "application/octet-stream";
}
/* Start: Set the MIME type, file name and meta detail in response */
response.setContentType(mimeType);
response.addHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", apFile.getName()));
response.addHeader("Content-Length", String.valueOf(apFile.length()));
/* End: Set the MIME type, file name and meta detail in response */
/* START: Read byte by byte and write to servlet output stream */
outputStream = response.getOutputStream();
int read;
final byte[] data = new byte[1 * 1024];
int i=0;
while ((read = in.read(data)) != -1) {
outputStream.write(data, 0, read);
i++;
slf4jLogger.debug("Writing data "+ i );
}
outputStream.flush();
in.close();
outputStream.close();
}
} catch (IOException ex) {
//Exception
}
}
My front end is in Angular 1.5. I am using file-saver lib to achieve.
Following is the code
function download() {
var url = apiHelper.getRootUrl() + '/request/' + '/download/' + vm.request.requestId;
$http.post(url, {responseType: "arraybuffer"}).success(
function (response, status, headers) {
var type = headers()['content-type'];
var disposition = headers()['content-disposition'];
if (disposition) {
var match = disposition.match(/.*filename=\"?([^;\"]+)\"?.*/);
if (match[1])
var defaultFileName = match[1];
}
defaultFileName = defaultFileName.replace(/[<>:"\/\\|?*]+/g, '_');
var blob = new Blob([response], {type: type});
saveAs(blob, defaultFileName);
});
}
I've got a problem with getInputStream from doPost request and setting filename before. It's giving me a file with right filename but empty file 0kB. If I'll comment setting fileName than I'll get not empty file.
My process: From android apps I'm archiving PDF file to upload server like http POST. At server doPost is like below method.
How to set filename and get not empty file?
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fileName = null;
//Get all the parts from request and write it to the file on server
//getFileName return filename
for (Part part : request.getParts()) {
fileName = getFileName(part);
}
File saveFile = new File(SAVE_DIR + fileName);
// opens input stream of the request for reading data
InputStream inputStream = request.getInputStream();
// opens an output stream for writing file
FileOutputStream outputStream = new FileOutputStream(saveFile);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.close();
inputStream.close();
// sends response to client
response.getWriter().print("UPLOAD DONE");
}
Edit:
private String getFileName(Part part) {
String contentDisp = part.getHeader("content-disposition");
System.out.println("content-disposition header= " + contentDisp);
String[] tokens = contentDisp.split(";");
for (String token : tokens) {
if (token.trim().startsWith("filename")) {
return token.substring(token.indexOf("=") + 2, token.length() - 1);
}
}
return "";
}
Solution:
//Get the right Part
final Part filePart = request.getPart("uploadedfile");
//Writes file to location
filePart.write(filePart.getSubmittedFileName());
Part offers a getInputStream() method, so you should use that instead of request.getInputStream(); when you're dealing with parts.
However...
Part.write() is a convenience method to write this uploaded item to disk, so you can just use part.write(SAVE_DIR + part.getSubmittedFileName()); and it's all handled for you (note that getSubmittedFileName() is available only for Servlet 3.1).
Do outputstream.flush() before doing outputstream.close()
I need to read a file (that is not available on web) on the server and output it to the user as a downloadable file.
The scenario is
The user click a link from an XPage
The request is sent to the server which reads a predefined file in the server file system
The file is brought back to the user as a downloadable file in the webbrowser.
The file on the server can be in any format, e.g .pdf, .exe, .doc etc
It does not matter if this is done on SSJS or in java.
I would really appreicate some code
Here is a similar question:
How to stream file from xPages?
And here is part of the Java code taken from there and completed by me (+a fix from you!). I have now tested it also and it works:
FacesContext facesContext = FacesContext.getCurrentInstance();
XspHttpServletResponse response = (XspHttpServletResponse) facesContext.getExternalContext().getResponse();
String strFileName = "myfile.txt";
String strFilePath= "c:" + File.separator + strFileName;
response.setContentType(URLConnection.guessContentTypeFromName(strFileName));
response.setHeader("Content-Disposition","attachment;filename=" + strFileName);
//File file = new File(strFilePath);
FileInputStream fileIn = new FileInputStream(strFilePath);
ServletOutputStream out = response.getOutputStream();
int iLen = 0;
byte[] btBuffer = new byte[10240]; // Not sure about optimal buffer size
while ((iLen = fileIn.read(btBuffer)) != -1) {
out.write(btBuffer, 0, iLen);
}
facesContext.responseComplete();
out.close();
You could do all this in SSJS also.
If guessContentTypeFromName does not guess it then you need to modify the definition file on server. Or if you have a limited set of file types you can place the MIME-type table in your code/application.
Here is the code I came up with to do this, def not production code.
public static byte[] grabFile(String readFile) throws IOException {
File file = new File(readFile);
ByteArrayOutputStream ous = new ByteArrayOutputStream();
InputStream ios = new FileInputStream(file);
try {
byte []buffer = new byte[4096];
int read = 0;
while ( (read = ios.read(buffer)) != -1 ) {
ous.write(buffer, 0, read);
}
} finally {
try {
if ( ous != null )
ous.close();
} catch ( IOException e) {
}
try {
if ( ios != null )
ios.close();
} catch ( IOException e) {
}
}
return ous.toByteArray();
}
public static void download() throws IOException {
byte[] data = grabFile("\\\\server\\path\\to\\file.pdf");
HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.reset();
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment; filename=\"filename.pdf\"");
OutputStream output = response.getOutputStream();
output.write(data);
output.close();
FacesContext.getCurrentInstance().responseComplete();
}
Then just call the download method from the beforeRenderResponse of your Xpage
I want to download office files,pdf files,image files,zip files,dll files,exe files based on the user selection.So, I want to download these file types from jsp page.
This is jsp code snippt:
<%
String filename = "Sample1.docx";
String filepath = "e:\\temp\\";
response.setContentType("APPLICATION/OCTET-STREAM");
response.setHeader("Content-Disposition","attachment; filename=\"" + filename + "\"");
java.io.FileInputStream fileInputStream = new java.io.FileInputStream(filepath + filename);
int i;
while ((i=fileInputStream.read()) != -1) {
out.write(i);
}
fileInputStream.close();
%>
But it raising some error when downloading office files, images files.When i open the downloaded files it says "The File may be Corrupted".
Is there any common way to download all types of files in jsp?
Your problem is that the out variable in a JSP is a JspWriter, which is a character stream and so your binary files get altered. You would be much better off using a servlet directly for this particular purpose.
ok, there are some issues when downloading files from different browsers. My example takes care the handling needed in MSIE and Mozilla type of browsers
public HttpServletResponse getFile (HttpServletRequest request ,HttpServletResponse httpServletResponse, .......){
HttpServletResponse response = httpServletResponse;
InputStream in =/*HERE YOU READ YOUR FILE AS BinaryStream*/
String filename = "";
String agent = request.getHeader("USER-AGENT");
if (agent != null && agent.indexOf("MSIE") != -1)
{
filename = URLEncoder.encode(/*THIS IS THE FILENAME SHOWN TO THE USER*/, "UTF8");
response.setContentType("application/x-download");
response.setHeader("Content-Disposition","attachment;filename=" + filename);
}
else if ( agent != null && agent.indexOf("Mozilla") != -1)
{
response.setCharacterEncoding("UTF-8");
filename = MimeUtility.encodeText(/*THIS IS THE FILENAME SHOWN TO THE USER*/, "UTF8", "B");
response.setContentType("application/force-download");
response.addHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
}
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
byte by[] = new byte[32768];
int index = in.read(by, 0, 32768);
while (index != -1) {
out.write(by, 0, index);
index = in.read(by, 0, 32768);
}
out.flush();
return response;
}
check this out
UPDATED
The problem is with JSP's "Out.write", which is not capable of writing byte stream...
Replaced jsp file with servlet...
The code snippet is:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String filename = (String) request.getAttribute("fileName");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment;filename="+filename);
File file = new File(filename);
FileInputStream fileIn = new FileInputStream(file);
ServletOutputStream out = response.getOutputStream();
byte[] outputByte = new byte[(int)file.length()];
//copy binary contect to output stream
while(fileIn.read(outputByte, 0, (int)file.length()) != -1)
{
out.write(outputByte, 0, (int)file.length());
}
}
Now U can download all types of files....