I own a mock project maven spring-rest with the end point
#RequestMapping(value = "/rest/{nid}/{fileName:.+}", method = RequestMethod.GET, produces = MediaType.APPLICATION_PDF_VALUE)
public String getPjSae(#PathVariable String nid, #PathVariable String fileName, HttpServletResponse response) throws IOException {
LOGGER.info("NID : " + nid);
LOGGER.info("NOM FICHIER : " + fileName);
File file = new File(saePath+File.separatorChar + fileName);
LOGGER.info("CHEMIN PJ : " + file);
if (file.exists()) {
InputStream inputStream = new FileInputStream(file); //load the file
// here I use Commons IO API to copy this file to the response output stream, I don't know which API you use.
IOUtils.copy(inputStream, response.getOutputStream());
// here we define the content of this file to tell the browser how to handle it
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".pdf");
response.flushBuffer();
}
return response.getOutputStream().toString();
}
I get the downloaded file and convert it to base64 to write it to an XML file. I have to make the comparison later and the problem is the writing of the signature of the object in the byte stream of the base 64: CoyoteOutputStream
At the end of each base64 of pdf I have a piece:
CnN0YXJ0eHJlZgo0NjkyOAolJUVPRgpvcmcuYXBhY2hlLmNhdGFsaW5hLmNvbm5lY3Rvci5Db3lvdGVPdXRwdXRTdHJlYW1ANDA4YTViYzI=
that is different each time because:
startxref
46928
%% EOF
org.apache.catalina.connector.CoyoteOutputStream#408a5bc2
So, this pit but comparison because : #408a5bc2 is unique
You eventually return this:
return response.getOutputStream().toString();
This returns the default toString output for the output stream in question, i.e. your CoyoteOutputStream object signature. If you want to avoid this, don't return it.
Related
I'm currently developping on a webserver for my company , and i'am facing a corrupted zip file problem. At the end of a servlet. Here's the function header called at the very end of the servlet.
sendFile(directory, "s" + this.optimizerId + "-" + this.optimizerName + "-" + start.toDate(Defs.DB.DATE_PATTERN) + "-" + end.toDate(Defs.DB.DATE_PATTERN), FileExtension.NONE, ContentType.ZIP, response);
Here is the first send File function ...
public default void sendFile(File file, String fileName, FileExtension extension, ContentType type, HttpServletResponse response) {
if(type == ContentType.ZIP) {
String[] files = file.list();
if(files != null && files.length > 0) {
byte[] bytes = FileUtils.getZipBytes(file, files);
sendFile(new String(bytes), fileName, extension, type, response);
}
}
}
The "FileUtils.getZipBytes(file, files)" function is doing fine , it returns a byteArray.
I even tried to create a zip file with ZipOutputStream a File , and on the server , the zip has litterally no problem.
( I will send the source code of this function if you think it's important , but i don't want to add useless complexity ).
Then , here's the code of the second sendFile function that is called from the first one.
public default void sendFile(String content, String fileName, FileExtension extension, ContentType type, HttpServletResponse response) {
response.setContentType(type.getExpression());
response.setHeader("Content-Disposition", "attachment; filename=\"" + FileUtils.encodeForFileName(fileName) + "." + extension.getExtension() + "\"");
OutputStream out = response.getOutputStream();
out.write(content.getBytes());
out.flush();
out.close();
}
Do you see any problem in these functions ?
The zip file is downloaded from the server. He is not empty (about 15.7KB).
But when i try to open it , the file explorer of Fedora tell me that the file is corrupted.
I also tried to use the unzip command on the file , and i have the following error message.
Archive: s192-Système U carquefou-2023-01-26 000000-2023-01-26 235959(1).zip
caution: zipfile comment truncated
error [s192-Système U carquefou-2023-01-26 000000-2023-01-26 235959(1).zip]: missing 3249308257 bytes in zipfile
(attempting to process anyway)
error [s192-Système U carquefou-2023-01-26 000000-2023-01-26 235959(1).zip]: attempt to seek before beginning of zipfile
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
I thought it may be the FileUtils.getZipBytes(file, files) that returns a corrupted ByteArray. But i really don't think it is ... Here's the source code of this function
public static byte[] getZipBytes(File directory, String[] files) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
byte bytes[] = new byte[2048];
for (String fileName : files) {
FileInputStream fis = new FileInputStream(directory.getPath() +
System.getProperty("file.separator") + fileName);
BufferedInputStream bis = new BufferedInputStream(fis);
zos.putNextEntry(new ZipEntry(fileName));
int bytesRead;
while ((bytesRead = bis.read(bytes)) != -1) {
zos.write(bytes, 0, bytesRead);
}
zos.closeEntry();
bis.close();
fis.close();
}
zos.flush();
baos.flush();
zos.close();
baos.close();
return baos.toByteArray();
}
A zip file is not a string. You should drop the round trip from byte array to string and back to byte array.
Declare your sendFile() method as
public default void sendFile(byte[] content, String fileName, FileExtension extension, ContentType type, HttpServletResponse response) {
response.setContentType(type.getExpression());
response.setHeader("Content-Disposition", "attachment; filename=\"" + FileUtils.encodeForFileName(fileName) + "." + extension.getExtension() + "\"");
OutputStream out = response.getOutputStream();
out.write(content);
out.flush();
out.close();
}
And call it as
sendFile(bytes, fileName, extension, type, response);
The problem with your approach is that new String(content).getBytes() does not return a byte array with the same content as you started with.
Sometime when I try to upload a file on my remote vps i get this exception (the upload proccess stop in 60%)
06-Jan-2016 11:59:36.801 SEVERE [http-nio-54000-exec-9] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [mvc-dispatcher] in context with path [] threw exception [Request processing failed;
nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request;
nested exception is org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. Unexpected EOF read on the socket]
with root cause
java.io.EOFException: Unexpected EOF read on the socket
and in Google Chrome the connextion is lost like the server is down, i get ERR_CONNECTION_ABORTED
i upload file like this in spring mvc
public void save_file(MultipartFile upfile , String path){
try {
File fichier = new File( path ) ;
byte[] bytes = upfile.getBytes();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream( fichier ));
stream.write(bytes);
stream.close();
System.out.println( "You successfully uploaded " + upfile.getOriginalFilename() + "!" );
} catch (Exception e) {
System.out.println( "You failed to upload " + upfile.getOriginalFilename() + " => " + e.getMessage() ); ;
}
}
my controller :
#RequestMapping(value = "/administration/upload", method = RequestMethod.POST)
public String Upload_AO_journal(
#ModelAttribute UploadForm uploadForm,
Model map , HttpServletRequest request, HttpSession session ) throws ParseException, UnsupportedEncodingException {
my bean
public class UploadForm {
...
public MultipartFile scan;
So how can solve this problem ?
Have you tried stream?
Jsp code:
<form method="POST" onsubmit="" ACTION="url?${_csrf.parameterName}=${_csrf.token}" ENCTYPE="multipart/form-data">
Controller:
#RequestMapping(
value = "url", method = RequestMethod.POST
)
public void uploadFile(
#RequestParam("file") MultipartFile file
) throws IOException {
InputStream input = upfile.getInputStream();
Path path = Paths.get(path);//check path
OutputStream output = Files.newOutputStream(path);
IOUtils.copy(in, out); //org.apache.commons.io.IOUtils or you can create IOUtils.copy
}
All that worked for me with spring 4.0 and spring security.
Secondly, you should check if the http connection is timeout. Chrome does not support that configuration. So you can use firefox and follow here http://morgb.blogspot.com.es/2014/05/firefox-29-and-http-response-timeout.html.
Not sure about the getBytes() method on the upfile. A more suitable approach would be to use the InputStream which will manage any size file and will buffer when necessary. Something like:
public void save_file(MultipartFile upfile , String path){
try {
File fichier = new File( path ) ;
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream( fichier ));
InputStream is = upfile.getInputStream();
byte [] bytes = new byte[1024];
int sizeRead;
while ((sizeRead = is.read(bytes,0, 1024)) > 0) {
stream.write(bytes, 0, sizeRead);
}
stream.flush();
stream.close();
System.out.println( "You successfully uploaded " + upfile.getOriginalFilename() + "!" );
} catch (Exception e) {
System.out.println( "You failed to upload " + upfile.getOriginalFilename() + " => " + e.getMessage() ); ;
}
}
This issue appears because you close stream until stream write whole data.
Wrong way:
stream.write(bytes);
stream.close();
Right way:
try (BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(fichier)))
{
stream.write(data);
}
You should close your stream after whole data is written.
I had similar issues and problem is when you are uploading file you are using Multipart Form POST Request. You can read about MIME .
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=frontier
This is a message with multiple parts in MIME format.
--frontier
Content-Type: text/plain
This is the body of the message.
--frontier
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg
Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==
--frontier--
Basically issue I had was that multipart from request has meta information part, text part and actual file encoded I believe in base64. Each of this parts are split by boundary. If you don't set up this boundary (in example above it's called "frontier") correctly server starts reading the file but doesn't know where it ends until it reaches EOF and returns error about unexpected EOF because it didn't found required boundaries.
Problem with your code is that you are writing file into ByteOutputStream which is suitable when returning file from server to the user not other way around. Server is expecting this Multipart request with some standard predefined formatting. Use Apache Commons HttpClient library that does all this request formating and boundary setting for you. If you use Maven then this link.
File f = new File("/path/fileToUpload.txt");
PostMethod filePost = new PostMethod("http://host/some_path");
Part[] parts = {
new StringPart("param_name", "value"),
new FilePart(f.getName(), f)
};
filePost.setRequestEntity(
new MultipartRequestEntity(parts, filePost.getParams())
);
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);
Disclaimer: code is not mine it's example from Apache website for multipart request
I got the same error when I didn't properly set the path where the file is going to be placed.
The fix was to change it like this:
factoryMaster.setCertificateFile("E:\\Project Workspace\\Live Projects\\fileStore\\Factory Master\\"+factoryMasterBean.getFile().getOriginalFilename());
and use throws exception in controller:
public #ResponseBody ResponseEntity<FactoryMaster> saveFactoryMaster(#ModelAttribute("factoryMasterBean") FactoryMasterBean factoryMasterBean,FactoryMaster factoryMaster) throws IllegalStateException, IOException {...............}
and make sure do not send any file with the same name which already exists.
I read the data from BLOB in Oracle, then assign to an object as byte []. I know the data is UTF-8 encoded XML therefore create a big String from the raw byte array and write directly to the PrintWriter of the ServletResponse object.
The response is expected to be downloaded on the client browser side as an attachment. But
the downloaded file is corrupt, the encoding is not correctly set in my opinion. Should I not set the HTTP Headers explicitly? I cannot figure out why I cannot reconstruct the XML as it is in the Database. any idea?
String filename = data_src.getFilename() + ".xml";
byte [] data = data_src.getByte_data();
String xml_UTF_8 = new String(data, "UTF-8");
// Init Servlet Response.
response.reset();
response.setHeader("Content-Type", "application/xml;charset=utf-8");
response.setHeader("Content-Encoding", "UTF-8");
response.setHeader("Content-Length", "" + xml_UTF_8.length() /*data.length ???*/);
response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\""); //inline vs attachment
PrintWriter writer = response.getWriter();
writer.write(xml_UTF_8);
writer.flush();
writer.close();
} finally {
// close streams.
response.flushBuffer();
}
// Inform JSF that it doesn't need to handle the response.
// This is very important, otherwise you will get the following exception in the logs:
// java.lang.IllegalStateException: Cannot forward after response has been committed.
facesContext.responseComplete();
}`
How to provide large files for download through spring controller ? I followed few discussions on similar topic :
Downloading a file from spring controllers
but those solutions fails for large files ~ 300mb - 600mb.
I am getting OutOfMemoryException on the last line :
#RequestMapping(value = "/file/{dummyparam}.pdf", method = RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
public #ResponseBody byte[] getFile(#PathVariable("dummyparam") String dummyparam, HttpServletResponse response) {
.
.
InputStream is = new FileInputStream(resultFile);
response.setHeader("Content-Disposition", "attachment; filename=\"dummyname " + dummyparam + ".pdf\"");
.
.
return IOUtils.toByteArray(is);
My (naive) assumption was that IOUtils will handle even large files but this is not obviously happening. Is there any way how to split file into chunks as download is in progress ? Files are usually around 300 - 600mb large. Max number of concurrent downloads is estimated to 10.
Easy way would be to link files as static content in the webserver directory but we would like to try do it in within our Spring app.
It is because you are reading the entire file into memory, use a buffered read and write instead.
#RequestMapping(value = "/file/{dummyparam}.pdf", method = RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void getFile(#PathVariable("dummyparam") String dummyparam, HttpServletResponse response) {
InputStream is = new FileInputStream(resultFile);
response.setHeader("Content-Disposition", "attachment; filename=\"dummyname " + dummyparam + ".pdf\"");
int read=0;
byte[] bytes = new byte[BYTES_DOWNLOAD];
OutputStream os = response.getOutputStream();
while((read = is.read(bytes))!= -1){
os.write(bytes, 0, read);
}
os.flush();
os.close();
}
For Spring , Need use InputStreamResource class in ResponseEntity .
Demo Code :
MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName);
System.out.println("fileName: " + fileName);
System.out.println("mediaType: " + mediaType);
File file = new File(DIRECTORY + "/" + fileName);
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
return ResponseEntity.ok()
// Content-Disposition
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
// Content-Type
.contentType(mediaType)
// Contet-Length
.contentLength(file.length()) //
.body(resource);
}
Ref Link : https://o7planning.org/en/11765/spring-boot-file-download-example
I have a Java Servlet that generates randomly thousands of Strings every time is called. I want the user to be able to get them in a file when he calls the Servlet. I don't want to write first the file on disk or memory.
Is there a way to write the file on the fly when the user calls the servlet?
Thanks
Any text that you generate in the Servlet can simply be written to the OutputStream returned by ServletResponse.getOutputStream().
If you want the output to be downloadable as a file, you can follow the approach in this answer - https://stackoverflow.com/a/11772700/1372207
The difference would be, that the Content-type would be text/plain and instead of reading from another inputstream, you would just write the String objects directly to the ServletOutputStream using the print(String) method.
If you use the idea to write content to HttpServletResponse's output stream while offering download service, rather than saving the content locally and then reading the file as FileInputStream, you can just convert the file content to InputStream by InputStream stream = new ByteArrayInputStream(exampleString.getBytes("UTF-8"));.
The following code partially references https://www.codejava.net/java-ee/servlet/java-servlet-download-file-example.
public void doDownload(HttpServletRequest request, HttpServletResponse response) throws IOException {
String fileName = "xxx.txt";
String fileContent = "";
// get absolute path of the application
ServletContext context = request.getServletContext();
// get MIME type of the file
String mimeType = context.getMimeType(fileName);
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
setResponseHeader(response, fileName, mimeType, (int) fileContent.length());
InputStream inputStream = new ByteArrayInputStream(fileContent.getBytes("UTF-8"));
// get output stream of the response
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[4096];
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);
}
inputStream.close();
outStream.close();
}
private void setResponseHeader(HttpServletResponse response, String fileName, String mimeType, Integer fileLength) {
response.setContentType(mimeType);
response.setContentLength(fileLength);
response.setContentType("application/octet-stream; charset=UTF-8");
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", fileName);
response.setHeader(headerKey, headerValue);
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
}