I'm using a library which wants a File() as an argument.
The file I want to pass it is one I want to package with my app, as part of the .jar
Is there any way to convert the JarEntry that I get from within my .jar to a File object I can pass?
If not and I have to copy the resource to disk temporarily, where's the best place to put the temporary file?
Thanks.
You cannot get a path to a file within a JARFile, only a stream, so you should extract it to the temporary directory and then pass that extracted file.
Here's a function I wrote to do this when I provided a db with a jar previously.
/**
* This method is responsible for extracting resource files from within the .jar to the temporary directory.
* #param filePath The filepath relative to the 'Resources/' directory within the .jar from which to extract the file.
* #return A file object to the extracted file
**/
public File extract(String filePath)
{
try
{
File f = File.createTempFile(filePath, null);
FileOutputStream resourceOS = new FileOutputStream(f);
byte[] byteArray = new byte[1024];
int i;
InputStream classIS = getClass().getClassLoader().getResourceAsStream("Resources/"+filePath);
//While the input stream has bytes
while ((i = classIS.read(byteArray)) > 0)
{
//Write the bytes to the output stream
resourceOS.write(byteArray, 0, i);
}
//Close streams to prevent errors
classIS.close();
resourceOS.close();
return f;
}
catch (Exception e)
{
System.out.println("An error has occurred while extracting the database. This may mean the program is unable to have any database interaction, please contact the developer.\nError Description:\n"+e.getMessage());
return null;
}
}
A File represents a real entry in the filesystem; a JarEntry doesn't exist on the file system. The mapping won't be there unless you extract the JAR entry to an actual file.
You can create a temp file using File.createTempFile. More details are available at this SO answer.
Related
This is my code, which I want to create method, that accept file and move it in my pc of specified folder. I just make this copy existing file of text to another text file, but I want to move in specified folder, not copy. How to solve this problem?
public static void main(String[] args) {
InputStream inStream = null;
OutputStream outStream = null;
try {
File afile = new File("C:\\Users\\anar.memmedov\\Desktop\\test.txt");
File bfile = new File("C:\\Users\\anar.memmedov\\Desktop\\ok\\test3.txt");
inStream = new FileInputStream(afile);
outStream = new FileOutputStream(bfile);
byte[] buffer = new byte[1024];
int length;
//copy the file content in bytes
while ((length = inStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
inStream.close();
outStream.close();
//delete the original file
afile.delete();
System.out.println("File is copied successful!");
} catch (IOException e) {
e.printStackTrace();
}
}
You can simply use Files.move: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#move(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)
Move or rename a file to a target file.
By default, this method attempts to move the file to the target file, failing if the target file exists except if the source and target are the same file, in which case this method has no effect. If the file is a symbolic link then the symbolic link itself, not the target of the link, is moved. This method may be invoked to move an empty directory. In some implementations a directory has entries for special files or links that are created when the directory is created. In such implementations a directory is considered empty when only the special entries exist. When invoked to move a directory that is not empty then the directory is moved if it does not require moving the entries in the directory. For example, renaming a directory on the same FileStore will usually not require moving the entries in the directory. When moving a directory requires that its entries be moved then this method fails (by throwing an IOException). To move a file tree may involve copying rather than moving directories and this can be done using the copy method in conjunction with the Files.walkFileTree utility method.
Path sourcePath = Paths.get("sourceFile.txt");
Path targetPath = Paths.get("targetFolder\\" + sourcePath.getFileName());
Files.move(sourcePath, targetPath);
I have below code where i am reading the file from particular directory, processing it and once processed i am moving the file to archive directory. This is working fine. I am receiving new file everyday and i am using Control-M scheduler job to run this process.
Now in next run i am reading the new file from that particularly directory again and checking this file with the file in the archive directory and if the content is different then only process the file else dont do anything. There is shell script written to do this job and we dont see any log for this process.
Now i want to produce log message in my java code if the files are identical from the particular directory and in the archive directory then generate log that 'files are identical'. But i dont know exactly how to do this. I dont want to write the the logic to process or move anything in the file ..i just need to check the files are equal and if it is then
produce log message. The file which i recieve are not very big and the max size can be till 10MB.
Below is my code:
for(Path inputFile : pathsToProcess) {
// read in the file:
readFile(inputFile.toAbsolutePath().toString());
// move the file away into the archive:
Path archiveDir = Paths.get(applicationContext.getEnvironment().getProperty(".archive.dir"));
Files.move(inputFile, archiveDir.resolve(inputFile.getFileName()),StandardCopyOption.REPLACE_EXISTING);
}
return true;
}
private void readFile(String inputFile) throws IOException, FileNotFoundException {
log.info("Import " + inputFile);
try (InputStream is = new FileInputStream(inputFile);
Reader underlyingReader = inputFile.endsWith("gz")
? new InputStreamReader(new GZIPInputStream(is), DEFAULT_CHARSET)
: new InputStreamReader(is, DEFAULT_CHARSET);
BufferedReader reader = new BufferedReader(underlyingReader)) {
if (isPxFile(inputFile)) {
Importer.processField(reader, tablenameFromFilename(inputFile));
} else {
Importer.processFile(reader, tablenameFromFilename(inputFile));
}
}
log.info("Import Complete");
}
}
Based on the limited information about the size of file or performance needs, something like this can be done. This may not be 100% optimized, but just an example. You may also have to do some exception handling in the main method, since the new method might throw an IOException:
import org.apache.commons.io.FileUtils; // Add this import statement at the top
// Moved this statement outside the for loop, as it seems there is no need to fetch the archive directory path multiple times.
Path archiveDir = Paths.get(applicationContext.getEnvironment().getProperty("betl..archive.dir"));
for(Path inputFile : pathsToProcess) {
// Added this code
if(checkIfFileMatches(inputFile, archiveDir); {
// Add the logger here.
}
//Added the else condition, so that if the files do not match, only then you read, process in DB and move the file over to the archive.
else {
// read in the file:
readFile(inputFile.toAbsolutePath().toString());
Files.move(inputFile, archiveDir.resolve(inputFile.getFileName()),StandardCopyOption.REPLACE_EXISTING);
}
}
//Added this method to check if the source file and the target file contents are same.
// This will need an import of the FileUtils class. You may change the approach to use any other utility file, or read the data byte by byte and compare. If the files are very large, probably better to use Buffered file reader.
private boolean checkIfFileMatches(Path sourceFilePath, Path targetDirectoryPath) throws IOException {
if (sourceFilePath != null) { // may not need this check
File sourceFile = sourceFilePath.toFile();
String fileName = sourceFile.getName();
File targetFile = new File(targetDirectoryPath + "/" + fileName);
if (targetFile.exists()) {
return FileUtils.contentEquals(sourceFile, targetFile);
}
}
return false;
}
I am currently making a game which have levels which are pre-made and i currently store them in resources. I want a solution to how i can extract a folder out of a jar in production and development environment.
I have tried copying the folder by using the given method below and pass the src as File defaultWorld = new File(GameData.class.getClassLoader().getResource("worlds/").getFile());
and destination as private static File worldsDir = new File("run/worlds");
public static void copyFolder(File src, File dest) {
try {
if (src.isDirectory()) {
if (!dest.exists()) {
dest.mkdir();
}
String[] files = src.list();
for (String file : files) {
copyFolder(new File(src, file), new File(dest, file));
}
} else {
try (InputStream in = new FileInputStream(src)) {
try (OutputStream out = new FileOutputStream(dest)) {
byte[] buffer = new byte[1024];
int length;
//copy the file content in bytes
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
I expected the above method to work on both dev and production env but it throws FileNotFoundException when opening file output stream.
You cannot list resources in a jar.
Any workarounds you’re thinking of, are unreliable.
Never call the getFile() method of URL. It does not return a valid file name; it just returns the path and query portions of the URL, with any percent-escapes intact. Furthermore, jar entries are not file: URLs, so a resource path can never be a valid file name when it refers to a jar entry.
The only way to list things in a jar file is by iterating through all jar entries, but you aren’t even guaranteed to have access to your jar, because ClassLoaders are not guaranteed to be URLClassLoaders, and in general are not guaranteed to use jar: URLs.
You can’t even rely on MyApplication.class.getProtectionDomain().getCodeSource(), because getCodeSource() can return null.
If you want to copy multiple files from your jar, here are some reliable ways to do it:
Hard code the list of resources you plan to copy. It’s your application, so you know what files you’re putting in the jar.
Keep a single text file in your jar which contains a list of resource paths to copy.
Store your resources in a single zip archive which is embedded in your jar, and extract it yourself with a ZipInputStream which wraps MyApplication.class.getResourceAsStream.
Assume I have a path:
/path/to/zipfile.zip/my/earfile.ear/plainfile
where zipfile.zip is a zip file, and earfile.ear is a ear file. The presence of those compressed files are arbitrary.
I want to have a method which takes this kind of path as param and return an input stream or reader for the 'plainfile'.
I heard that it is easier to implement in Java 7 but I don't know how.
Thank you in advance.
I have come up with a way to solve this problem with restricted condition, which is that there are exactly two zip files are allowed.
public BufferedReader testNestedZipEntry() {
try {
// first zip file wrapped by ZipFile, which allows random access(just feed a path!) to find an entry
ZipFile zipFile = new ZipFile("/path/to/zipfile.zip");
// second zip file wrapped by ZipInputStream, which is sequential so you have to iterate over entries to find the one you need
ZipEntry jarZipEntry = zipFile.getEntry("earfile.ear");
ZipInputStream jarZipIn = new ZipInputStream(zipFile.getInputStream(jarZipEntry));
ZipEntry curEntry = null;
while( (curEntry=jarZipIn.getNextEntry()) != null){
// getNextEntry() method "positions" the input stream to curEntry
// this is the reason why we can achieve this without extracting the file out
if(curEntry.getName().equals("plainfile")) {
return new BufferedReader(new InputStreamReader(jarZipIn));
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
Tip for beginners: Entries in a zip file is all the files you can reach in the whole zip file, not only the ones in the root directory of the zip file.
I want to upload files and save them into specific directory.And i am new to files concept.When i uploading files from my page they are saved in another directory(C:\Users\ROOTCP~1\AppData\Local\Temp\multipartBody989135345617811478asTemporaryFile) and not in specified directory.I am unable to set it.Please help me in finding a solution.For all help thanks in advance.
public static Result uploadHoFormsByHeadOffice() throws Exception {
Logger.info("#C HoForms -->> uploadHoFormsByHeadOffice() -->> ");
final String basePath = System.getenv("INVOICE_HOME");
play.mvc.Http.MultipartFormData body = request().body()
.asMultipartFormData(); // get Form Body
StringBuffer fileNameString = new StringBuffer(); // to save file path
// in DB
String formType = body.asFormUrlEncoded().get("formType")[0];// get formType from select Box
FilePart upFile = body.getFile("hoFiles");//get the file details
String fileName = upFile.getFilename();//get the file name
String contentType = upFile.getContentType();
File file = upFile.getFile();
//fileName = StringUtils.substringAfterLast(fileName, ".");
// path to Upload Files
File ftemp= new File(basePath +"HeadOfficeForms\\"+formType+"");
//File ftemp = new File(basePath + "//HeadOfficeForms//" + formType);
File f1 = new File(ftemp.getAbsolutePath());// play
ftemp.mkdirs();
file.setWritable(true);
file.setReadable(true);
f1.setWritable(true);
f1.setReadable(true);
//HoForm.create(fileName, new Date(), formType);
Logger.info("#C HoForms -->> uploadHoFormsByHeadOffice() <<-- Redirecting to Upload Page for Head Office");
return redirect(routes.HoForms.showHoFormUploadPage());
}
}
I really confused why the uploaded file is saved in this(C:\Users\ROOTCP~1\AppData\Local\Temp\multipartBody989135345617811478asTemporaryFile) path.
You're almost there.
File file = upFile.getFile(); is the temporary File you're getting through the form input. All you've got to do is move this file to your desired location by doing something like this: file.renameTo(ftemp).
Your problem in your code is that you're creating a bunch of files in memory ftemp and f1, but you never do anything with them (like writing them to the disk).
Also, I recommend you to clean up your code. A lot of it does nothing (aforementioned f1, also the block where you're doing the setWritable's). This will make debugging a lot easier.
I believe when the file is uploaded, it is stored in the system temporary folder as the name you've provided. It's up to you to copy that file to a name and location that you prefer. In your code you are creating the File object f1 which appears to be the location you want the file to end up in.
You need to do a file copy to copy the file from the temporary folder to the folder you want. Probably the easiest way is using the apache commons FileUtils class.
File fileDest = new File(f1, "myDestFileName.txt");
try {
FileUtils.copyFile(ftemp, fileDest);
}
catch(Exception ex) {
...
}