Spring MVC Safe way to Upload, generate and download a file - java

I´m working on a WebApp with Spring MVC and Maven. I have the following process: First of all the User has to upload a file. Afterwards the uploaded file will be edited. Last but not least I want to create a download which contains the edited file.
The first step "Upload File" works well. I have a controller which contains the following POST method:
#RequestMapping(value = "/CircleUp", method = RequestMethod.POST)
public String circleUpPost(HttpServletRequest request, Model model, //
#ModelAttribute("circleUpForm") CircleUpForm circleUpForm) {
return this.doUpload(request, model, circleUpForm);
}
private String doUpload(HttpServletRequest request, Model model, //
CircleUpForm circleUpForm) {
File file = circleUpForm.getFile();
if (file != null) {
try {
//Some edit stuff
serialize(file, SerializationModeEnum.Standard);
} catch (Exception e) {
e.printStackTrace();
}
}
model.addAttribute("uploadedFiles", file);
return "uploadResult";
}
protected static String serialize(File file, SerializationModeEnum serializationMode) {
java.io.File test = null;
try {
test = java.io.File.createTempFile("Test", ".pdf");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
file.save(test, serializationMode);
} catch (Exception e) {
e.printStackTrace();
}
// test.deleteOnExit();
return test.getPath();
}
In the "serialize" Method my PDFClown File will be saved to a temp folder.
Afterwards the "uploadResult" page will be appear which contains the folloing code:
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<meta charset="UTF-8">
<title>Download</title>
</head>
<body>
<h3>Download Files:</h3>
CircleUp
</body>
</html>
When the User clicks on the link another Controller will be called which handles the download. I dont know how to design the controller so that it can works with the edited file which I saved in my temp folder. I think it should look like that :
#RequestMapping(value = "/Download")
public void download(HttpServletRequest request, HttpServletResponse response) throws IOException {
final String temperotyFilePath = ???
String fileName = "Test.pdf";
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment; filename=" + fileName);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos = convertPDFToByteArrayOutputStream(temperotyFilePath + "\\" + fileName);
OutputStream os = response.getOutputStream();
baos.writeTo(os);
os.flush();
} catch (Exception e1) {
e1.printStackTrace();
}
}
private ByteArrayOutputStream convertPDFToByteArrayOutputStream(String fileName) {
InputStream inputStream = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
inputStream = new FileInputStream(fileName);
byte [] buffer = new byte [1024];
baos = new ByteArrayOutputStream();
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return baos;
}
I have two questions now:
How can the DownloadController attain the temp path to the file?
Is this process of Uploading,Generating and Downloading a File safe? Or is there a better way to handle this process?
I´m new to Spring MVC and WebApp Development and I´m thankful for every suggestion :)

You can use the same approach you use in the upload
test = java.io.File.createTempFile("Test", ".pdf");
All you need is to point to the same file and then read it.
If you need a custom dir for the files saving you can either define a property - my.file.path=some path here or
use system temp dir
public class Main {
public static void main(String[] args) {
String property = "java.io.tmpdir";
String tempDir = System.getProperty(property);
System.out.println("OS current temporary directory is " + tempDir);
}
}
Got the code from the link
Actually the approach is not safe. What to do if 2 different users upload files with the same name& What if one is uploaded and another user tries to download it? What is amount of files is millions? etc. etc.
It's better to use independent file storage but for test project it's fine

Related

java.nio.file.Files.delete(path) method throws java.nio.file.FileSystemException, when trying to remove file after creating and using it

My scenario is the folowing, I'm getting some data from db, writing it to a file, then giving that file by inputStrem to the client response.
Here is the code doing this:
public void getErrorsFile(UUID jobId, Pageable pageable, HttpServletResponse response) {
List<ValidationErrorsDTO> failedList = getValidationErrors(jobId, pageable);
String fileName = "failedRows.txt";
PrintWriter writer;
Path path = null;
try {
writer = new PrintWriter(fileName, "UTF-8");
for (ValidationErrorsDTO dto : failedList) {
String line = dto.getLineNumber() + ": " + dto.getValidationMessage() + "\n";
writer.println(line);
}
writer.flush();
path = Paths.get(fileName);
} catch (IOException e) {
e.printStackTrace();
}
try (InputStream inputStream = Files.newInputStream(path)) {
writeStreamToResponse(response, inputStream, fileName);
IOUtils.closeQuietly(inputStream);
Files.delete(path);
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeStreamToResponse(HttpServletResponse response, InputStream inputStream, String fileName) throws IOException {
response.addHeader("Content-disposition", "attachment;filename=" + fileName);
response.setContentType("txt/plain");
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
}
And when running this code, all the phases go well, except the one, which removes the file at the end.And I get this exception
java.nio.file.FileSystemException: failedRows.txt: The process cannot access the file because it is being used by another process.
The speps I did for solving the issue:
I did put the process of removing file in another thread, thinking that it's a concurrency issue.
No other process is holding this file somehow I think.
I did try another way of removing the file, used java.io.file.delete
method.
I did put the process of removing file in finally block.
Nothing helps.
Will appreciate any useful suggestion.
You should have open the writer instance as try-with-resources similar to opening the input stream:
try (writer = new PrintWriter(fileName, "UTF-8")) {
for (ValidationErrorsDTO dto : failedList) {
String line = dto.getLineNumber() + ": " + dto.getValidationMessage() + "\n";
writer.println(line);
}
writer.flush();
path = Paths.get(fileName);
} catch (IOException e) {
e.printStackTrace();
}

Unable to visualize pdf file after submitting via FilePond component in React to a servlet(backend)

I have a front React application with an upload document form via FilePond library and as backend I use Java to save the pdf document to my local files.
I am a little bit stucked beacause I have the pdf file and I can see the content(in Notepad/Code) but when I open it with Adobe/Microsoft Edge all pages are blank I don't know why.
In FilePond documentation it's not specified the defaut encoding and I don't know there is a way to see the encoding in the request(req).Also if it's not possible in this way how th send the file with getFileEncodeBase64String. Thank you very much for any ideas.
Code in React :
const APIClient = axios.create({
// baseURL: 'https://postman-echo.com',
baseURL: Config.faqServerUrl,
timeout: Config.timeout,
headers: {
'Accept': 'application/json;charset=UTF-8',
'Content-Type': 'application/json;charset=UTF-8'
}
});
Call backend :
function processFile(fieldName, file, metadata, load, error, progress, abort, setFileName) {
const formData = new FormData();
let feedbackWS = null;
formData.append('file', file, file.name);
try{
feedbackWS = APIClient.post('/upload-document',formData, {
onUploadProgress: (e) => {
// updating progress indicator
progress(e.lengthComputable, e.loaded, e.total);
}
}).then((response) => {
load(response.data);
setFileName(response.data);
})
.catch((error) => {
console.error(error);
});
} catch (error) {
console.log(error);
}
}
FilePond component :
<FilePond
server={{
process:(fieldName, file, metadata, load, error, progress, abort) => {
processFile(fieldName, file, metadata, load, error, progress, abort);
}
}}
oninit={() => handleInit()}
// callback onupdatefiles- a file has been added or removed, receives a list of file items
onupdatefiles={(fileItems) => {
// Set current file objects to this.state
setFile(fileItems.map(fileItem => fileItem.file));
}}
instantUpload={true}
onprocessfile={(error, file)=>{
console.log('PROCESSED', file, 'SERVER_ID', file.serverId);
// console.log('ERROR PROCESSED', error);
}}
onaddfile={(error, file)=>{
console.log('PROCESSED', file, 'SERVER_ID', file.serverId);
// console.log('ERROR PROCESSED', error);
}}
/>
Java code :
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("encoding" + req.getCharacterEncoding()); // it is null
nom_upload_faq = "test.pdf";
String repertoireFaqTemp = "/temp/";
String pathComplet = repertoireFaqTemp + nom_upload_faq;
File fichierTemp = null;
try {
fichierTemp = new File(pathComplet);
if (fichierTemp.exists() && fichierTemp.isFile()) {
fichierTemp.delete();
}
if (fichierTemp.createNewFile()) {
fichierTemp.setReadable(true, false);
fichierTemp.setWritable(true, false);
fichierTemp.setExecutable(true, false);
fichierTemp.deleteOnExit();
} else {
System.out.println("Impossible d'arriver ici on l'a déjà supprimer avant");
}
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
//byte[] fileAsBytes = getArrayFromInputStream(req.getInputStream());
byte[] fileAsBytes = null;
try {
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(req);
for (FileItem item: items) {
if(!item.isFormField()) { // it'a file
fileAsBytes = getArrayFromInputStream(item.getInputStream());
}
}
} catch (FileUploadException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(fichierTemp))) {
output.write(fileAsBytes);
output.flush();
output.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static byte[] getArrayFromInputStream(InputStream inputStream) throws IOException {
byte[] bytes = null;
byte[] buffer = new byte[1024];
try (BufferedInputStream is = new BufferedInputStream(inputStream)) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int length;
while ((length = is.read(buffer)) > -1) {
bos.write(buffer, 0, length);
}
bos.flush();
bytes = bos.toByteArray();
} catch (Exception e) {
System.out.println("toto");
}
return bytes;
}
Edit: Thank you Rik indeed the call formData.append('file', file, file.name); is the right one but still the same problem with the encoding(adding ? symbol) this is why I thought that it will be maybe the only possiblity to send the pdf file as base64 I've installed the plugin but as it'a asyncronous I can't call the methods getFileEncodeBase64String and in the servlet the request stream it's not the right one as you can see in the picture.
here you can see the sequence before the mouse selection I have an 8 with ?
You don't have to encode the file
formData.append('file', file.getFileEncodeBase64String(), file.name);
You can do:
formData.append('file', file, file.name);
And your browser will post a file object which you can handle like any other file posted with a form. See: https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects

Upload file using Spark framework and FreeMarker

I'm trying to make a method to upload files using the spark framework and freemarker but I seem to be hitting a brick wall at the .getPart method. My current freemarker code looks as follows:
<form method='post' enctype='multipart/form-data'>
<div>
<input type='file' name='uploadedFile'>
<button>Upload csv</button>
</div>
</form>
and my spark java post method code looks as follows:
post(new Route("/pdf", "multipart/form-data") {
#Override
public Object handle(Request request, Response response) {
String name = null;
File upLoadM = new File("messages/");
Path tempFile = null;
Part file = null;
try {
tempFile = Files.createTempFile(upLoadM.toPath(), "", "");
System.out.println(tempFile);
}
catch (IOException e1) {
e1.printStackTrace();
}
request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp"));
try {
file = request.raw().getPart("uploadedFile");
System.out.println(file);
}
catch (IOException | ServletException e1) {
e1.printStackTrace();
}
try (InputStream input = file.getInputStream()) {
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException e) {
e.printStackTrace();
}
response.status(201);
response.redirect("/pdf");
return "";
}
});
When i hit the upload button I get a 500 Internal Error. Not sure what the reason it's crashing at the .getPart method is. Any help would be appreciated.
Turns out it is working if i tweek the line of code:
request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp"));
to
request.attribute("org.eclipse.multipartConfig", new MultipartConfigElement("/temp"));

Spring + GWTP: Excel file download from OutputStream not working

I have a GWTP application to export some data to an .xlsx file using Apache POI. Here is my presenter code.
protected void exportTable(String selectedPublisher, String selectedTarget, Drilldown drilldown) {
this.excelServiceAsync.generateDataEnrichmentExport(selectedPublisher, new AsyncCallback<Void>() {
#Override
public void onSuccess(Void result) {
Window.open(GWT.getModuleBaseURL() + "rpc/excelDownload", "_blank", "");
}
#Override
public void onFailure(Throwable caught) {
Window.alert("Error!");
}
});
}
Here is my excel generation code which is asynchronously called by the presenter.
workbook = new XSSFWorkbook();
sheetOne = workbook.createSheet("Export One");
// TODO Typical POI coding stuff
FileOutputStream out = null;
try {
out = new FileOutputStream(new File("file.xlsx"));
workbook.write(out);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
And here is my controller which I use to download the excel file.
#ResponseBody
#RequestMapping(value = "/excelDownload")
public String downloadExcel(HttpServletRequest request, HttpServletResponse response){
File file = new File("file.xlsx");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename="
+ "file.xlsx");
response.setHeader("Content-Length", String.valueOf(file.length()));
FileInputStream is = null;
try {
is = new FileInputStream(file);
IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
response.getOutputStream().close();
} catch (FileNotFoundException e) {
logger.error(e.getMessage(), e.getCause());
} catch (IOException e) {
logger.error(e.getMessage(), e.getCause());
} finally {
try {
is.close();
} catch (Exception ex) {
return "redirect:/error/internal";
}
}
return "";
}
I'm generating the excel file and putting on the output stream. Then in the controller I get the data from the output stream to download as an .xlsx file. This isn't working. The working file is generated and saved on the server. But the file that is downloaded by the browser is a corrupted file. I'm not really sure why. Please help!
You forgot to close your FileOutputStream out. Maybe try using the try (resource) {...} syntax of Java7?
Try set response.setContentLength((int) file.length());

Download and save a file using Java

Let's say I have an URL, like something.domain/myfile.txt then I want to save this file, with that "Save File" dialog.
I tried my best to do it, but everytime I save the file using the dialog the file is not there.
An example or somewhere I can find information on this would help a lot!
URL website = null;
try {
website = new URL(<insert url here>);
} catch (MalformedURLException e) {
e.printStackTrace();
}
ReadableByteChannel rbc = null;
try {
rbc = Channels.newChannel(website.openStream());
} catch (IOException e2) {
e2.printStackTrace();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("minecraft.jar"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
try {
fos.getChannel().transferFrom(rbc, 0, 1 << 24);
} catch (IOException e) {
e.printStackTrace();
}
JFileChooser fileChooser = new JFileChooser();
if (fileChooser.showSaveDialog(fileChooser) == JFileChooser.APPROVE_OPTION) {
File dir = fileChooser.getCurrentDirectory();
dir.mkdir();
//After this point is where I need help.
I trust that this is what you're looking for:
if (fileChooser.showSaveDialog(fileChooser) == JFileChooser.APPROVE_OPTION)
{
File file = fileChooser.getSelectedFile();
// whatever you want to do with the file
System.out.println("The file is "+file.getAbsolutePath());
// fos = new FileOutputStream(file) ...
}
Did you notice that in your code you are trying to save/download the file before giving the user the option to chose the destination?
I would split the code into three different operations:
A method in charge of transferring the bytes from the InputStream (the web) to the OutputStream (the file).
a method to show the user a dialog so he can chose where to store the file.
the method that completes the whole task: choose a file and transfer the bytes from the web to it.
1) Would be something like this (you don't need to use the NIO API to implement it):
public void transfer(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
}
2) would be something very similar to what Dukeling has already stated:
public File chooseFile() {
File result = null;
JFileChooser fileChooser = new JFileChooser();
if (fileChooser.showSaveDialog(fileChooser) == JFileChooser.APPROVE_OPTION) {
result = fileChooser.getSelectedFile();
}
return result;
}
3) then, combining these two operations together is really simple:
public void saveFileFromWeb(URL url) {
File file = chooseFile(); // 1. Choose the destination file
if (file != null) {
// 2. Create parent folder structure
File folder = file.getParentFile();
if (!folder.exist()) {
folder.mkdirs();
}
InputStream in = null;
OutputStream out = null;
try {
// 3. Initialise streams
in = url.openStream();
out = new FileOuputStream(file);
// 4. Transfer data
transfer(in, out);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. close streams
if (in != null) {
try {
in.close();
} catch (IOException e) { /* ignore */ }
}
if (out != null) {
try {
out.close();
} catch (IOException e) { /* ignore */ }
}
}
}
NOTE: 1) and 2) could be private methods. Of course you could do this is just one single operation but splitting it would give you an overview of the different steps to perform.
NOTE 2: I simplified the exception handling part

Categories