I have a application that current allows a user to upload large sets of data to a web service. The problem is that these files take some time to upload over the network and so I would like to allow the user to zip the file first and then upload it.
#RequestMapping(value = "upload", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<?> uploadObjects(HttpServletRequest request,
#RequestParam("file") MultipartFile file) {
//Do stuff with it
}
I can currently unzip the MultipartFile into a Java IO file, but all the existing logic only works with a MultipartFile and would require some (potentially a lot of) reworking.
private File unzip(MultipartFile file) throws IOException {
byte[] buffer = new byte[1024];
int bufferSize = 1024;
File tempFile = null;
ZipInputStream zis = new ZipInputStream(file.getInputStream());
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
tempFile = File.createTempFile(entry.getName(), "tmp");
tempFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempFile);
BufferedOutputStream bos = new BufferedOutputStream(fos, bufferSize);
int count;
while ((count = zis.read(buffer, 0, bufferSize)) != -1) {
bos.write(buffer, 0, count);
}
bos.flush();
bos.close();
}
zis.close();
return tempFile;
}
Is there a way to unzip a MultipartFile back into a MultipartFile? Or convert a File into a MultipartFile?
try org.springframework.mock.web.MockMultipartFile,
it is intended to be used in tests, so it can be created locally.
There is a constructor
public MockMultipartFile(String name, InputStream contentStream)
which could fit your needs....
Related
in spring boot project, I want to download all the files of he user into a single zip.
#GetMapping("/allFilePerson/{personId}")
public ResponseEntity<HttpStatus> allFilePerson(#PathVariable(value = "personId") Integer personId) {
List<file> fileList=service.findByFileId(personId);
....
}
This code single file download.But convert to fileList zip or rar.
#RequestMapping("/downloadFile/{fileId}")
public ResponseEntity<HttpStatus> handleFileDownloadPage(HttpServletRequest request, HttpServletResponse response, #PathVariable(value = "fileId") Integer fileId) throws IOException, Exception {
File file = service.getFile(fileId);
ServletOutputStream out = response.getOutputStream();
InputStream stream = null;
stream = new FileInputStream(file.getFilePath());
//write the file to the file specified
int bytesRead = 0;
byte[] buffer = new byte[8192];
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", String.format(" attachment; filename=\"%s\"", file.getFileName()));
while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
out.close();
return ResponseEntity.ok(HttpStatus.OK);
}
I have this program which I am trying to use to create a zip file of the file located in the directory.
The program runs but in chrome it fails to download by saying the network error.
In Mozilla, it says Ut0ij4ld.ZIP.part could not be saved, because the source file could not be read.
what am I doing wrong, is there a better approach to do this?
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = "D:\\Test\\";
File directory = new File(path);
String[] files = directory.list();
//check if directories have files
if (files != null && files.length > 0) {
//create zip stream
byte[] zip = zipFiles(directory, files);
// Sends the response back to the user / browser with zip content
ServletOutputStream sos = response.getOutputStream();
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"DATA.ZIP\"");
sos.write(zip);
sos.flush();
}
request.setAttribute("DownloadMessage", "Successfully");
request.getRequestDispatcher("DownloadZipFile.jsp").forward(request, response);
}
private byte[] zipFiles(File directory, String[] files) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
byte bytes[] = new byte[4096];
for (String fileName : files) {
try (FileInputStream fis = new FileInputStream(directory.getPath()
+ "/" + 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();
}
}
zos.flush();
baos.flush();
zos.close();
baos.close();
return baos.toByteArray();
}
This works,
#Override
protected void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//set the content type to zip
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"DATA.ZIP\"");
//to write it over http
ServletOutputStream ouputStream = response.getOutputStream();
//for writing files in the ZIP file format. Includes support for both compressed and uncompressed entries
ZipOutputStream zos= new ZipOutputStream(ouputStream);
//your file root folder
File rootFolder= new File ("D:\\Test\\") ;
// Looping through all the files
for (File file: rootFolder.listFiles()){
try {
writeToZip(zos,file);
} catch (Exception ex) {
Logger.getLogger(Zipper.class.getName()).log(Level.SEVERE, null, ex);
}
}
zos.close();
ouputStream.close();
}
private static void writeToZip(ZipOutputStream zos,File file) throws Exception{
FileInputStream fis=new FileInputStream(file);
ZipEntry zipEntry= new ZipEntry(file.getName());
zos.putNextEntry(zipEntry);
final byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
} }
I want to compress multiples files into a zip files, I'm dealing with big files, and then download them into the client, for the moment I'm using this:
#RequestMapping(value = "/download", method = RequestMethod.GET, produces = "application/zip")
public ResponseEntity <StreamingResponseBody> getFile() throws Exception {
File zippedFile = new File("test.zip");
FileOutputStream fos = new FileOutputStream(zippedFile);
ZipOutputStream zos = new ZipOutputStream(fos);
InputStream[] streams = getStreamsFromAzure();
for (InputStream stream: streams) {
addToZipFile(zos, stream);
}
final InputStream fecFile = new FileInputStream(zippedFile);
Long fileLength = zippedFile.length();
StreamingResponseBody stream = outputStream - >
readAndWrite(fecFile, outputStream);
return ResponseEntity.ok()
.header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + "download.zip")
.contentLength(fileLength)
.contentType(MediaType.parseMediaType("application/zip"))
.body(stream);
}
private void addToZipFile(ZipOutputStream zos, InputStream fis) throws IOException {
ZipEntry zipEntry = new ZipEntry(generateFileName());
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
}
This take a lot of time before all files are zipped and then the downloading start, and for large files this kan take a lot of time, this is the line responsible for the delay:
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
So is there a way to download files immediately while their being zipped ?
Try this instead. Rather than using the ZipOutputStream to wrap a FileOutputStream, writing your zip to a file, then copying it to the client output stream, instead just use the ZipOutputStream to wrap the client output stream so that when you add zip entries and data it goes directly to the client. If you want to also store it to a file on the server then you can make your ZipOutputStream write to a split output stream, to write both locations at once.
#RequestMapping(value = "/download", method = RequestMethod.GET, produces = "application/zip")
public ResponseEntity<StreamingResponseBody> getFile() throws Exception {
InputStream[] streamsToZip = getStreamsFromAzure();
// You could cache already created zip files, maybe something like this:
// String[] pathsOfResourcesToZip = getPathsFromAzure();
// String zipId = getZipId(pathsOfResourcesToZip);
// if(isZipExist(zipId))
// // return that zip file
// else do the following
StreamingResponseBody streamResponse = clientOut -> {
FileOutputStream zipFileOut = new FileOutputStream("test.zip");
ZipOutputStream zos = new ZipOutputStream(new SplitOutputStream(clientOut, zipFileOut));
for (InputStream in : streamsToZip) {
addToZipFile(zos, in);
}
};
return ResponseEntity.ok()
.header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + "download.zip")
.contentType(MediaType.parseMediaType("application/zip")).body(streamResponse);
}
private void addToZipFile(ZipOutputStream zos, InputStream fis) throws IOException {
ZipEntry zipEntry = new ZipEntry(generateFileName());
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
}
public static class SplitOutputStream extends OutputStream {
private final OutputStream out1;
private final OutputStream out2;
public SplitOutputStream(OutputStream out1, OutputStream out2) {
this.out1 = out1;
this.out2 = out2;
}
#Override public void write(int b) throws IOException {
out1.write(b);
out2.write(b);
}
#Override public void write(byte b[]) throws IOException {
out1.write(b);
out2.write(b);
}
#Override public void write(byte b[], int off, int len) throws IOException {
out1.write(b, off, len);
out2.write(b, off, len);
}
#Override public void flush() throws IOException {
out1.flush();
out2.flush();
}
/** Closes all the streams. If there was an IOException this throws the first one. */
#Override public void close() throws IOException {
IOException ioException = null;
for (OutputStream o : new OutputStream[] {
out1,
out2 }) {
try {
o.close();
} catch (IOException e) {
if (ioException == null) {
ioException = e;
}
}
}
if (ioException != null) {
throw ioException;
}
}
}
For the first request for a set of resources to be zipped you wont know the size that the resulting zip file will be so you can't send the length along with the response since you are streaming the file as it is zipped.
But if you expect there to be repeated requests for the same set of resources to be zipped, then you can cache your zip files and simply return them on any subsequent requests; You will also know the length of the cached zip file so you can send that in the response as well.
If you want to do this then you will have to be able to consistently create the same identifier for each combination of the resources to be zipped, so that you can check if those resources were already zipped and return the cached file if they were. You might be able to could sort the ids (maybe full paths) of the resources that will be zipped and concatenate them to create an id for the zip file.
In my app I'm zipping and then downloading larges files, the files are located in azure, so I read the files from a stream and then zip them one after another, so I can dowload the zip file after all files has been zipped, here's my code:
#RequestMapping(value = "{analyseId}/download", method = RequestMethod.GET, produces = "application/zip")
public ResponseEntity<Resource> download(#PathVariable List<String> paths) throws IOException {
String zipFileName = "zipFiles.zip";
File zipFile = new File(zipFileName);
FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
for (String path : paths) {
InputStream fis = azureDataLakeStoreService.readFile(path);
addToZipFile(path , zos, fis);
}
zos.close();
fos.close();
BufferedInputStream zipFileInputStream = new BufferedInputStream(new FileInputStream(zipFile.getAbsolutePath()));
InputStreamResource resource = new InputStreamResource(zipFileInputStream);
zipFile.delete();
return ResponseEntity.ok()
.header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + zipFileName)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(resource);
}
private static void addToZipFile(String path, ZipOutputStream zos, InputStream fis) throws IOException {
ZipEntry zipEntry = new ZipEntry(FilenameUtils.getName(path));
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
}
However on azure the request time out is set to 230 sec, and cannot be changed, however for big files it takes more than that to load and then zip the files on the server, so the connection with the client will be lost meanwhile.
So my question is since I'm getting the data from a stream, can we do all these operations simultaneously, means getting the stream and download it as the same time and not waiting till getting the whole file, or if there any other idea can any body share it here please.
Thanks.
The answer is to not download the file to the server and then send it to the client but streaming it to the client directly here's the code
#RequestMapping(value = "/download", method = RequestMethod.GET)
public StreamingResponseBody download(#PathVariable String path) throws IOException {
final InputStream fecFile = azureDataLakeStoreService.readFile(path);
return (os) -> {
readAndWrite(fecFile, os);
};
}
private void readAndWrite(final InputStream is, OutputStream os)
throws IOException {
byte[] data = new byte[2048];
int read = 0;
while ((read = is.read(data)) >= 0) {
os.write(data, 0, read);
}
os.flush();
}
I also added this configuration to ApplicationInit:
#Configuration
public static class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(-1);
configurer.setTaskExecutor(asyncTaskExecutor());
}
#Bean
public AsyncTaskExecutor asyncTaskExecutor() {
return new SimpleAsyncTaskExecutor("async");
}
}
I use this Java code to download files from a web application:
#RequestMapping(value = "/filedownloads/filedownload/{userid}/{projectid}/{documentfileid}/{version}/", method = RequestMethod.GET)
public void filesDownload(final #PathVariable("userid") String userId, final #PathVariable("projectid") String projectId,
final #PathVariable("documentfileid") String documentFileId, final #PathVariable("version") String version,
final HttpServletResponse response) throws IOException, BusinessException {
...
final String fileName = "filename=" + documentFile.getFileName();
final InputStream is = new FileInputStream(filePath);
response.setHeader("Content-Disposition", "inline; " + fileName);
IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
}
if I will download a pptx- file I get the following IE- page:
What I want to do is to open the downloaded file in Powerpoint.
My question now would be if there is a header setting in order to open this file with the right application (in this case Powerpoint)
Simply try to set the Content Type header properly which is application/vnd.openxmlformats-officedocument.presentationml.presentation in case a pptx, as next:
response.setContentType(
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
);
response.setHeader(
"Content-Disposition",
String.format("inline; filename=\"%s\"", documentFile.getFileName())
);
response.setContentLength((int) new File(filePath).length());
Here is the list of mime types corresponding to Office 2007 documents.
Here is a little sample code from a Spring MVC Controller:
#RequestMapping("/ppt")
public void downloadPpt(HttpServletRequest request, HttpServletResponse response) throws IOException {
Resource resource = new ClassPathResource("Presentation1.pptx");
InputStream resourceInputStream = resource.getInputStream();
response.setHeader("Content-Disposition", "attachment; filename=\"Presentation1.pptx\"");
response.setContentLengthLong(resource.contentLength());
byte[] buffer = new byte[1024];
int len;
while ((len = resourceInputStream.read(buffer)) != -1) {
response.getOutputStream().write(buffer, 0, len);
}
}
By setting the Content-Disposition to attachment, you're telling the browser to download this file as an attachment and by supplying the correct file name with extension, you're telling the Operating System to use whatever application the user normally uses to open a file of this type. In this case it will be MS Power Point.
This way you can get away with not knowing exactly what version of Power Point the file was created with.
I have tested code in IE-11 its work fine. See below code i.e
#RequestMapping(value = "/downloadfile", method = RequestMethod.GET)
#ResponseBody
public void downloadfile(HttpServletRequest request, HttpServletResponse response) throws Exception {
ServletOutputStream servletOutputStream = null;
try {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=downloadppt.pptx");
byte[] ppt = downloadFile();
servletOutputStream = response.getOutputStream();
servletOutputStream.write(ppt);
} catch (Exception e) {
throw e;
} finally {
servletOutputStream.flush();
servletOutputStream.close();
}
}
Generate bytes from saved pptx file.
public byte[] downloadFile() throws IOException {
InputStream inputStream = new FileInputStream(new File("e:/testppt.pptx"));
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// Transfer bytes from source to destination
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
byteArrayOutputStream.write(buf, 0, len);
}
inputStream.close();
byteArrayOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
That's it, you are able to download pptx file. Hope code help you, if you have any query or doubt then we can discuss or if any suggestions. Thank you