I'm using apache's FileUpload in order to upload some files to my webserver. Problem is I don't want to upload them to a specific location on the machine i.e : c:\tmp, but rather to a relative path such as /ProjectName/tmp/
Here's my code:
private static final long serialVersionUID = 1L;
private String TMP_DIR_PATH = "c:\\tmp";
private File tmpDir;
private static final String DESTINATION_DIR_PATH ="/files";
private File destinationDir;
public void init(ServletConfig config) throws ServletException {
super.init(config);
tmpDir = new File(TMP_DIR_PATH);
if(!tmpDir.isDirectory()) {
throw new ServletException(TMP_DIR_PATH + " is not a directory");
}
String realPath = getServletContext().getRealPath(DESTINATION_DIR_PATH);
destinationDir = new File(realPath);
if(!destinationDir.isDirectory()) {
throw new ServletException(DESTINATION_DIR_PATH+" is not a directory");
}
}
I want to change TMP_DIR_PATH so it's relative to my project, help would be appreciated!
If your actual concern is the hardcoding of the c:\\tmp part which makes the code unportable, then consider using File#createTempFile() instead. This will create the file in the platform default temp location as specified by the java.io.tmpdir system property.
File file = File.createTempFile("upload", ".tmp");
OutputStream output = new FileOutputStream(file);
// ...
If you want the temp directory to be relative to your project you'll have to use getRealPath and then make sure you create the directory if it doesn't already exist.
You can also use File dir = (File) getServletContext().getAttribute("javax.servlet.context.tempdir"); to fetch an app-specific temp directory provided by the container.
Related
I have the following prefix:
String prefix = TemplatesReader.class.getClassLoader().getResource("templates/").getPath();
and have method
public byte[] read(String pathToTemplate) {
return Files.readAllBytes(Paths.get(prefix + pathToTemplate));
}
in intellij idea works correctly, but when starting jar an error occurs:
java.nio.file.NoSuchFileException: file:/app.jar!/BOOT-INF/classes!/templates/request-orders/unmarked/RequestOrderUnmarked.pdf
You must not assume that a resource is a file. When the resource is inside a .jar file, it is a part of that .jar file; it is no longer a separate file at all.
You cannot use Files or Paths to read the resource.
You cannot use the getPath() method of URL. It does not return a file name. It only returns the path portion of the URL (that is, everything between the URL’s scheme/authority and its query portion), which is not a file path at all.
Instead, read the resource using getResourceAsStream:
private static final String RESOURCE_PREFIX = "/templates/";
public byte[] read(String pathToTemplate)
throws IOException {
try (InputStream stream = TemplatesReader.class.getResource(
RESOURCE_PREFIX + pathToTemplate)) {
return stream.readAllBytes();
}
}
I am able to upload multiple files to s3 bucket at once. However there is a mismatch in the file name the one I provided and uploaded file. I am interested in file name as I need to generate cloud front signed url based on that.
File generation code
final String fileName = System.currentTimeMillis() + pictureData.getFileName();
final File file = new File(fileName); //fileName is -> 1594125913522_image1.png
writeByteArrayToFile(img, file);
AWS file upload code
public void uploadMultipleFiles(final List<File> files) {
final TransferManager transferManager = TransferManagerBuilder.standard().withS3Client(amazonS3).build();
try {
final MultipleFileUpload xfer = transferManager.uploadFileList(bucketName, null, new File("."), files);
xfer.waitForCompletion();
} catch (InterruptedException exception) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("InterruptedException occurred=>" + exception);
}
} catch (AmazonServiceException exception) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("AmazonServiceException occurred =>" + exception);
}
throw exception;
}
}
Uploaded file name is 94125913522_image1.png. As you can see first two characters disappeared. What am I missing here. I am not able to figure out. Kindly advice.
private static void writeByteArrayToFile(final byte[] byteArray, final File file) {
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(Paths.get(file.getName())))) {
outputStream.write(byteArray);
} catch (IOException exception) {
throw new FileIllegalStateException("Error while writing image to file", exception);
}
}
The reason of the problem
You lose the first two charecters of the file names because of the third argument of this method:
transferManager.uploadFileList(bucketName, null, new File("."), files);
What happens in this case
So, what is the third argument:
/**
...
* #param directory
* The common parent directory of files to upload. The keys
* of the files in the list of files are constructed relative to
* this directory and the virtualDirectoryKeyPrefix.
...
*/
public MultipleFileUpload uploadFileList(... , File directory, ...){...}
And how will it be used:
...
int startingPosition = directory.getAbsolutePath().length();
if (!(directory.getAbsolutePath().endsWith(File.separator)))
startingPosition++;
...
String key = f.getAbsolutePath().substring(startingPosition)...
Thus, the directory variable is used to define a starting index to trim file paths to get file keys.
When you pass new File(".") as a directory, the parent directory for your files will be {your_path}.
But this is a directory, and you need to work with files inside it. So the common part, retrieved from your directory file, is {your_path}./
That is 2 symbols more than you actually need. And for this reason this method trims the 2 extra characters - an extra shift of two characters when trimming the file path.
The solution
If you only need to work with the current directory, you can pass the current directory as follows:
MultipleFileUpload upload = transferManager.uploadFileList(bucketName, "",
System.getProperty("user.dir"), files);
But if you start working with external sources, it won't work. So you can use this code, which creates one MultipleFileUpload per group of files from one directory.
private final String PATH_SEPARATOR = File.separator;
private String bucketName;
private TransferManager transferManager;
public void uploadMultipleFiles(String prefix, List<File> filesToUpload){
Map<File, List<File>> multipleUploadArguments =
getMultipleUploadArguments(filesToUpload);
for (Map.Entry<File, List<File>> multipleUploadArgument:
multipleUploadArguments.entrySet()){
try{
MultipleFileUpload upload = transferManager.uploadFileList(
bucketName, prefix,
multipleUploadArgument.getKey(),
multipleUploadArgument.getValue()
);
upload.waitForCompletion();
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
private Map<File, List<File>> getMultipleUploadArguments(List<File> filesToUpload){
return filesToUpload.stream()
.collect(Collectors.groupingBy(this::getDirectoryPathForFile));
}
private File getDirectoryPathForFile(File file){
String filePath = file.getAbsolutePath();
String directoryPath = filePath.substring(0, filePath.lastIndexOf(PATH_SEPARATOR));
return new File(directoryPath);
}
I have the following directory layout:
src
main
java
resources
sql (scripts for database)
spring (configuration)
webapp
Within a ServletContextListener class, I want to access the files under the SQL directory and list them. Basically my problem is with the path, because I know that listing files under a directory in a nutshell is:
File folder = new File(path);
File[] listOfFiles = folder.listFiles();
Maybe I could use the ServletContextEvent Object to try and build a path to resources/sql
public void contextInitialized(ServletContextEvent event) {
event.getServletContext(); //(getRealPath etc.)
}
Does something exist to set that path in a relative, non-hardcoded way?
Something like new File("classpath:sql") (preferably spring if possible) or what should I do with the servletContext to point at resources/sql?
I'm assuming the contents of src/main/resources/ is copied to WEB-INF/classes/ inside your .war at build time. If that is the case you can just do (substituting real values for the classname and the path being loaded).
URL sqlScriptUrl = MyServletContextListener.class
.getClassLoader().getResource("sql/script.sql");
Finally, this is what I did:
private File getFileFromURL() {
URL url = this.getClass().getClassLoader().getResource("/sql");
File file = null;
try {
file = new File(url.toURI());
} catch (URISyntaxException e) {
file = new File(url.getPath());
} finally {
return file;
}
}
...
File folder = getFileFromURL();
File[] listOfFiles = folder.listFiles();
import org.springframework.core.io.ClassPathResource;
...
File folder = new ClassPathResource("sql").getFile();
File[] listOfFiles = folder.listFiles();
It is worth noting that this will limit your deployment options, ClassPathResource.getFile() only works if the container has exploded (unzipped) your war file.
Just use com.google.common.io.Resources class. Example:
URL url = Resources.getResource("file name")
After that you have methods like: .getContent(), .getFile(), .getPath() etc
Consider the code sample below. Migrator class takes two input files, processes it and writes the output to final.tbl.
I want final.tbl to be created on the same path where the folder of input files is present.
Also the execute method should take relative path of this generated final.tbl file.
public class Migrator{
public void Migrate(String path1,String path2){
PrintStream out = new PrintStream("final.tbl");//I need relative path as that of input folder path i.e path1,path2
//.....
//.....Processing
}
}
class MainProcess{
public execute(String path){
//here the execute method should the the relative path of above final.tbl file
}
public static void main(String args[]){
}
}
Path path = Paths.get(path1);
PrintStream out = new PrintStream(path.getParent().toString() + "\\final.tbl");
I think you can use getAbsolutePath to get path to your input files:
public class Migrator{
public void Migrate(String path1,String path2){
File f = new File(path1);
String absolutePath = f.getAbsolutePath(); // use absolutePath for your PrintStream
PrintStream out = new PrintStream(absolutePath);//I need relative path as that of input folder path i.e path1,path2
//.....
//.....Processing
}
}
Hope it helped
Use the getParentFile()
File target = new File(new File(path1).getParentFile(), "final.tbl");
PrintStream out = new PrintStream(target);
This question already has answers here:
Recommended way to save uploaded files in a servlet application
(2 answers)
Closed 7 years ago.
My goal is to upload an image and save inside images folder to Web Content Folder.
{Please see the image below}
and save the image path into database
{Please see the image below}
I encountered some problem at this link
I tried to do but I did not managed to save the image to images/users folder.
And I realize that the save path is store at my C DRIVE. What if I change my computer? Will the image will be there?
Below are my codes.
Help will be appreciate.. Thanks! :)
In jsp
<input type="file" name="file">
In servlet
public class AServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String SAVE_DIR = "WebContent\\images\\users";
.........
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// gets absolute path of the web application
String appPath = request.getServletContext().getRealPath("");
// constructs path of the directory to save uploaded file
String savePath = appPath + File.separator + SAVE_DIR;
// creates the save directory if it does not exists
File fileSaveDir = new File(savePath);
if (!fileSaveDir.exists()) {
fileSaveDir.mkdir();
}
for (Part part : request.getParts()) {
String fileName = extractFileName(part);
part.write(savePath + File.separator + fileName);
}
System.out.println(savePath);
}
private String extractFileName(Part part) {
String contentDisp = part.getHeader("content-disposition");
String[] items = contentDisp.split(";");
for (String s : items) {
if (s.trim().startsWith("filename")) {
return s.substring(s.indexOf("=") + 2, s.length()-1);
}
}
return "";
}
Follow the link provided by SasiKathimanda for code implementation.
Create the file name as 'OrignalFilename+SystemTimeInMills' in order to avoid overriding already uploaded file with same filename.
Otherwise check for duplicate filename before write filecontent to server.
Follow the pattern(For DB File Path) if you need to provide download option(later), otherwise you can save the uploaded absolute file path.