I have a tar file and which contains many files. I need to get a specific file from tar file and read data from that file.
I am untaring file using the below code and I will read this returned input stream using some other function.
private InputStream unTar(final File inputFile, final File outputDir) throws FileNotFoundException, IOException, ArchiveException {
InputStream versionInputStream = null;
final InputStream is = new FileInputStream(inputFile);
final TarArchiveInputStream debInputStream = (TarArchiveInputStream) new ArchiveStreamFactory().createArchiveInputStream("tar", is);
TarArchiveEntry entry = null;
while ((entry = (TarArchiveEntry)debInputStream.getNextEntry()) != null) {
if (!entry.isDirectory() && entry.getName().equals("version.txt")) {
versionInputStream = new FileInputStream(entry.getFile());
}
}
return versionInputStream;
}
I get null pointer exception when i do versionInputStream = new FileInputStream(entry.getFile());
I know that we can first save this file in directory and then read the file but i dont want to save this file in directory.
Is there some way I can read this file without saving the file to some dir?
There is no file for an entry of an archive you read. TarArchiveEntry's getFile method only returns anything useful when the entry has been created with a File-arg constructor, which only makes sense when creating an archive not reading it.
The stream you are looking for is the TarArchiveInputStream itself after you've positioned it at the entry you want to read, i.e.
if (!entry.isDirectory() && entry.getName().equals("version.txt")) {
versionInputStream = debInputStream;
break;
}
note the break.
The not-yet-released (an no, no release date, yet) Commons Compress 1.21 will contain a new TarFile class that provides random-access to archives read from a seekable source (like a File) and will make your task more convenient.
Related
I have a zip archive that contains several gzip files. But gzip file's extentions are also .zip . I walk through zip archive with ZipInputStream. How can I detect inner file's type with reading its content rather than extentions. I also need not to change (or reset) ZipInputStream position.
So I need;
Read files in zip with using inputStream (ZipInputStream in my case) Because zip in zip is possible.
Find file type from its content.
While finding file type from its content, inputStream position should not change. Because i will continue to read next files.
Example:
root/1.zip/2.zip/3.zip(actually 3 is gzip)/4.txt
Sample Java Code:
public static void main(String[] args) {
//root/1.zip/2.zip/3.zip(actually 3 is gzip)/4.txt
String file = "root/1.zip";
File rootZip = new File(file);
try (FileInputStream fis = new FileInputStream(rootZip)) {
lookupInZip(fis)
.stream()
.forEach(System.out::println);
} catch (IOException e) {
System.out.println("Failed to get files");
}
}
public static List<String> lookupInZip(InputStream inputStream) throws IOException {
Tika tika = new Tika();
List<String> paths = new ArrayList<>();
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry entry = zipInputStream.getNextEntry();
while (entry != null) {
String entryName = entry.getName();
if (!entry.isDirectory()) {
//Option 1
//String fileType = tika.detect(entryName);
//Option 2
String fileType = tika.detect(zipInputStream);
if ("application/zip".equals(fileType)) {
List<String> innerPaths = lookupInZip(zipInputStream);
paths.addAll(innerPaths);
} else {
paths.add(entryName);
}
}
entry = zipInputStream.getNextEntry();
}
return paths;
}
If I use option 1, '3.zip' is evaluated as zip file but it is gzip.
If I use option 2, '2.zip' is evaluated as zip correctly by using its content. But when lookupInZip() is called for '3.zip' recursively, zipInputStream.getNextEntry() returns null. Because in previous step, we use inputStream content to detect type and inputStrem position changed.
Note: tika.detect() uses BufferedInputStream in implementation to reset inputStream position but it does not solve my problem.
The first two bytes are enough to see if it is likely a zip file, likely a gzip file, or certainly something else.
If the first two bytes are 0x50 0x4b, then it is likely a zip file. If the first two bytes are 0x1f 0x8b, then it is likely a gzip file. If it is neither, then the file is something else.
The first two bytes matching is not a guarantee it is that type, but it appears from your structure that it is usually one or the other, and you can use the extension as further corroborating evidence that it is compressed.
As for not changing the position, you need a way to peek at the first two bytes without advancing the position, or a way to get them and then unget them to return the position to where it was.
I have used InputStream to succesfully copy a file from one location to another:
public static void copy(File src, File dest) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("C:\\test.txt");
os = new FileOutputStream("C:\\javatest\\test.txt");
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buf)) > 0) {
os.write(buf, 0, bytesRead);
}
} finally {
is.close();
os.close();
}
}
The problem appears when I add a non-existing folder into the path, for example:
os = new FileOutputStream("C:\\javatest\\javanewfolder\\test.txt");
This returns a NullPointerException error. How can I create all of the missing directories when executing the copy process through Output Stream?
First, if possible I'd recommend you to use the java.nio.file classes (e.g. Path), instead of the File based approach. You will create Path objects by using a file system. You may use the default filesystem, if no flexibility is needed here:
final String folder = ...
final String filename = ...
final FileSystem fs = FileSystems.getDefault();
final Path myFile fs.getPath(folder, filename);
Then your problem is easily solved by a very convenient API:
final Path destinationFolder = dest.getParent();
Files.createDirectories(myPath.getParent());
try (final OutputStream os = Files.newOutputStream(myFile)) {
...
}
The Files.createDirectories() method will not fail if the directory already exists, but it may fail due to other reasons. For example if a file "foo/bar" exists, Files.createDirectories("foo/bar/folder") will most likely not succeed. ;)
Please read the javadoc carefully!
To check, if a path points to an existing directory, just user:
Files.isDirectory(somePath);
If needed, you can convert between File and Path. You will lose file system information, though:
final Path path1 = file1.toPath();
final File file2 = path2.toFile();
You could use Files.createDirectories:
Files.createDirectories(Paths.get("C:\\javatest\\javanewfolder"));
Also, you could use Files.copy to copy file )
I'm having trouble reading a text file from inside a jar. I managed it while it was unzipped but now that I've archived it it won't work.
The jar has:
BTA.jar
Splash.class
*.class
Splash.txt
etc..
Now, at the moment I'm using a BufferedReader and a FileReader
f = new FileReader("Splash.txt");
in = new BufferedReader(f);
but from googling around I know that that only reads files from outside of the jar.
My question is: How do I read a file from inside the jar?
Assuming you're executing the BTA.jar, you can use
InputStream in = YourClass.class.getResourceAsStream("/Splash.txt");
to retrieve an InputStream and pass it to the BufferedReader through an InputStreamReader.
The rules for the format of the path to pass to the method are defined here, in the javadoc for the Class class.
The problem is Splash.txt isn't a file, its series of bytes within in a (lightly) compressed file which has a named entry within a table of contents as specified by the Jar/Zip standard.
This measn you can use FileReader to read the file, instead, you need to access it via the Class resource management API, for example..
getClass().getResourceAsStream("/Splash.txt");
Or, if you're trying to read it from static context
Splash.class.getResourceAsStream("/Splash.txt");
Don't forget, you become responsible for closing the InputStream when you are done, for example...
try (InputStream is = getClass().getResourceAsStream("/Splash.txt")) {....
A jar is just a zip file and can be read like this..
ZipFile file = new ZipFile("/home/Desktop/myjar.jar");
Enumeration<? extends ZipEntry> entries = file.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.getName().startsWith("splash")) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(entry)))) {
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
}
I have a zip file (x.zip) in which there is another zipfile (y.zip). I need to read a file in y.zip. How can I iterate through both the zip files to read the file?
The code I am using to iterate x.zip to read y.zip is as below.
In the code, "zipX" represents "x.zip". When "y.zip" is encountered, it satisfies the "if condition" in the code. Here I need to iterate through "zipEntry" and read a file in that.
How can this be achieved?
private void getFileAsBytes(String path, String name) throws IOException {
ZipFile zipX = new ZipFile(path);
Enumeration<? extends ZipEntry> entries = zipX.entries();
while (entries.hasMoreElements())
{
ZipEntry zipEntry = entries.nextElement();
if(zipEntry.getName().contains(name) && zipEntry.getName().endsWith(".zip")) {
InputStream is;
is = zipX.getInputStream(zipEntry);
// Need to iterate through zipEntry here and read data from a file inside it.
break;
}
}
zipX.close();
}
According to the ZipFile docs, you need to pass in a File object or a file path; an InputStream isn't supported.
With that in mind, you could write that InputStream to a temporary file and then pass that file in to your existing method:
...
is = zipX.getInputStream(zipEntry);
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
//For production, generate a unique name for the temp file instead of using "temp"!
File tempFile = createTempFile("temp", "zip", tmpDir);
this.getFileAsBytes(tempFile.getPath(), name);
break;
...
I need to create a File object out of a file path to an image that is contained in a jar file after creating a jar file. If tried using:
URL url = getClass().getResource("/resources/images/image.jpg");
File imageFile = new File(url.toURI());
but it doesn't work. Does anyone know of another way to do it?
To create a file on Android from a resource or raw file I do this:
try{
InputStream inputStream = getResources().openRawResource(R.raw.some_file);
File tempFile = File.createTempFile("pre", "suf");
copyFile(inputStream, new FileOutputStream(tempFile));
// Now some_file is tempFile .. do what you like
} catch (IOException e) {
throw new RuntimeException("Can't create temp file ", e);
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
Don't forget to close your streams etc
This should work.
String imgName = "/resources/images/image.jpg";
InputStream in = getClass().getResourceAsStream(imgName);
ImageIcon img = new ImageIcon(ImageIO.read(in));
Usually, you can't directly get a java.io.File object, since there is no physical file for an entry within a compressed archive. Either you live with a stream (which is best most in the cases, since every good API can work with streams) or you can create a temporary file:
URL imageResource = getClass().getResource("image.gif");
File imageFile = File.createTempFile(
FilenameUtils.getBaseName(imageResource.getFile()),
FilenameUtils.getExtension(imageResource.getFile()));
IOUtils.copy(imageResource.openStream(),
FileUtils.openOutputStream(imageFile));
You cannot create a File object to a reference inside an archive. If you absolutely need a File object, you will need to extract the file to a temporary location first. On the other hand, most good API's will also take an input stream instead, which you can get for a file in an archive.