I have one file example.tar.gz and I need to copy it to another location with different name
example _test.tar.gz. I have tried with
private void copyFile(File srcFile, File destFile) throws IOException {
InputStream oInStream = new FileInputStream(srcFile);
OutputStream oOutStream = new FileOutputStream(destFile);
// Transfer bytes from in to out
byte[] oBytes = new byte[1024];
int nLength;
BufferedInputStream oBuffInputStream = new BufferedInputStream(oInStream);
while((nLength = oBuffInputStream.read(oBytes)) > 0) {
oOutStream.write(oBytes, 0, nLength);
}
oInStream.close();
oOutStream.close();
}
where
String from_path = new File("example.tar.gz");
File source = new File(from_path);
File destination = new File("/temp/example_test.tar.gz");
if(!destination.exists())
destination.createNewFile();
and then
copyFile(source, destination);
It doesn't work. The path is correct. It prints that the file exists. Can anybody help me?
Why to reinvent the wheel, just use FileUtils.copyFile(File srcFile, File destFile) , this will handle many scenarios for you
I would suggest Apache commons FileUtils or NIO (direct OS calls)
or Just this
Credits to Josh - standard-concise-way-to-copy-a-file-in-java
File source=new File("example.tar.gz");
File destination=new File("/temp/example_test.tar.gz");
copyFile(source,destination);
Updates:
Changed to transferTo from #bestss
public static void copyFile(File sourceFile, File destFile) throws IOException {
if(!destFile.exists()) {
destFile.createNewFile();
}
FileChannel source = null;
FileChannel destination = null;
try {
source = new RandomAccessFile(sourceFile,"rw").getChannel();
destination = new RandomAccessFile(destFile,"rw").getChannel();
long position = 0;
long count = source.size();
source.transferTo(position, count, destination);
}
finally {
if(source != null) {
source.close();
}
if(destination != null) {
destination.close();
}
}
}
There is Files class in package java.nio.file. You can use the copy method.
Example: Files.copy(sourcePath, targetPath).
Create a targetPath object (which is an instance of Path) with the new name of your file.
Related
I'm running into a problem using the commons compress library to create a tar.gz of a directory. I have a directory structure that is as follows.
parent/
child/
file1.raw
file2.raw
file3.raw
I hope the compressed structure is like this.
child/
file1.raw
file2.raw
file3.raw
Is there any way to remove the outermost layer during compression?
I've seen such examples, but I can't work properly,and can only handle fixed name structures
public static void main(String[] args) throws IOException {
String hallFilePath = "E:/" + "packs";
compress(Paths.get(hallFilePath).toString(), hallFilePath + ".zip");
}
public static void compress(String fromPath, String toPath) throws IOException {
File fromFile = new File(fromPath);
File toFile = new File(toPath);
if (!fromFile.exists()) {
throw new ServiceException(fromPath + "不存在!");
}
try (FileOutputStream outputStream = new FileOutputStream(toFile); CheckedOutputStream checkedOutputStream = new CheckedOutputStream(outputStream, new CRC32()); ZipOutputStream zipOutputStream = new ZipOutputStream(checkedOutputStream)) {
String baseDir = "";
compress(fromFile, zipOutputStream, baseDir);
}
}
private static void compress(File file, ZipOutputStream zipOut, String baseDir) throws IOException {
if (file.isDirectory()) {
compressDirectory(file, zipOut, baseDir);
} else {
if (baseDir.equals("packs" + File.separator)) {
baseDir = File.separator;
} else if (baseDir.equals("packs" + File.separator + "examineeInfo" + File.separator)) {
baseDir = "examineeInfo" + File.separator;
}
compressFile(file, zipOut, baseDir);
}
}
private static void compressFile(File file, ZipOutputStream zipOut, String baseDir) throws IOException {
if (!file.exists()) {
return;
}
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
ZipEntry entry = new ZipEntry(baseDir + file.getName());
zipOut.putNextEntry(entry);
int count;
byte[] data = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1) {
zipOut.write(data, 0, count);
}
}
}
private static void compressDirectory(File dir, ZipOutputStream zipOut, String baseDir) throws IOException {
File[] files = dir.listFiles();
if (files != null && ArrayUtils.isNotEmpty(files)) {
for (File file : files) {
compress(file, zipOut, baseDir + dir.getName() + File.separator);
}
}
}
My file structure:
This is how it looks using netbeans project:
-src
-images
-*.jpg
-stock
-*.java
-images (exact copy of -images)
and here is my jar
-jar
-images
-*.jpg
-stock
-*.java
-images (folder is created but files don't get copied)
My files imagesCopy is the one that I create and ImagesOrg is the one inside .jar / src
File imagesCopy = new File("images");
File imagesOrg = new File(URLDecoder.decode(getClass().getResource("/images").getPath()));
if (!imagesCopy.exists()) {
imagesCopy.mkdir();
for(final File child : imagesOrg.listFiles()) {
try{
Files.copy(child.toPath(), Paths.get(imagesCopy.getAbsolutePath()+"/"+child.getName()), REPLACE_EXISTING);
}catch(Exception e){
System.out.println(e);
}
}
}
The problem definitely lies with:
File imagesOrg = new File(URLDecoder.decode(getClass().getResource("/images").getPath()));
When compiling it it gives me, which is the proper directory
D:\Code\build\classes\images
which is the right directory, but when using this program from jar file I get:
D:\Code\dist\file:\D:\Code\dist\egz.jar!\images
and I assume that it should just be:
D:\Code\dist\egz.jar!\images
without that first part
Probably the simplest way to do it is like this:
public static void main(String[] args) throws URISyntaxException, IOException {
File imagesCopy = new File("C:\\Users\\<YOURNAMEHERE>\\images");
URI uri = ImageCopy.class.getResource("/images").toURI();
if (!uri.toString().startsWith("file:")) {
Map<String, String> env = new HashMap<>();
env.put("create", "true");
FileSystems.newFileSystem(uri, env);
}
Path imagesOrg = Paths.get(uri);
System.out.println(imagesOrg);
if (!imagesCopy.exists()) {
imagesCopy.mkdir();
try(DirectoryStream<Path> paths = Files.newDirectoryStream(imagesOrg)) {
for (final Path child : paths) {
System.out.println(child);
try {
String targetPath = imagesCopy.getAbsolutePath() + File.separator + child.getFileName().toString();
System.out.println(targetPath);
Files.copy(child, Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
It's not super-pretty, but it works. Might need to fiddle with the code if you have nested directories.
Note that you must create the FileSystem before accessing it (as per the Oracle Docs). I don't know why this is required, but there we go.
I've tested this and it will copy files from inside your JAR to wherever you would like.
Here is a simple code to do it. You can adapt as you need.
package br.com.jjcampos.main;
//imports here
public class CopyImage {
private static ClassLoader loader = CopyImage.class.getClassLoader();
public static void main(String[] args) throws IOException {
InputStream stream = loader.getResourceAsStream("br/com/jjcampos/images/test.jpg");
OutputStream outputStream =
new FileOutputStream(new File("c:/temp/newImage.jpg"));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.close();
}
}
Understand that you can't copy a source from a stream (your jar) as a list of files. Unless you want to unpack it first. My suggestion is you to add a txt file with the list of your images then you read this file and use suggested code to copy each one.
Something like this:
public class CopyImage {
private static ClassLoader loader = CopyImage.class.getClassLoader();
public static void main(String[] args) throws IOException {
copyImages("c:/temp/");
}
public static void copyImages(String pathDestiny) throws IOException{
InputStream listOfFiles = loader
.getResourceAsStream("br/com/jjcampos/images/listImages.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(listOfFiles));
String line;
while ( (line = reader.readLine())!=null ){
InputStream stream = loader.getResourceAsStream("br/com/jjcampos/images/"
+ line);
OutputStream outputStream =
new FileOutputStream(new File(pathDestiny + line));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.close();
}
}
}
And your listImages.txt with
test.jpg
And you should decide if you put the full path on the text file or not to use in your code.
I've found the Resources.readLines() and Files.readLines() to be helpfull in simplifiying my code.
The problem is that I often read gzip-compressed txt-files or txt-files in zip archives from URL's (HTTP and FTP).
Is there a way to use Guava's methods to read from these URL's too? Or is that only possible with Java's GZIPInputStream/ZipInputStream?
You can create your own ByteSources:
For GZip:
public class GzippedByteSource extends ByteSource {
private final ByteSource source;
public GzippedByteSource(ByteSource gzippedSource) { source = gzippedSource; }
#Override public InputStream openStream() throws IOException {
return new GZIPInputStream(source.openStream());
}
}
Then use it:
Charset charset = ... ;
new GzippedByteSource(Resources.asByteSource(url)).toCharSource(charset).readLines();
Here is the implementation for the Zip. This assumes that you read only one entry.
public static class ZipEntryByteSource extends ByteSource {
private final ByteSource source;
private final String entryName;
public ZipEntryByteSource(ByteSource zipSource, String entryName) {
this.source = zipSource;
this.entryName = entryName;
}
#Override public InputStream openStream() throws IOException {
final ZipInputStream in = new ZipInputStream(source.openStream());
while (true) {
final ZipEntry entry = in.getNextEntry();
if (entry == null) {
in.close();
throw new IOException("No entry named " + entry);
} else if (entry.getName().equals(this.entryName)) {
return new InputStream() {
#Override
public int read() throws IOException {
return in.read();
}
#Override
public void close() throws IOException {
in.closeEntry();
in.close();
}
};
} else {
in.closeEntry();
}
}
}
}
And you can use it like this:
Charset charset = ... ;
String entryName = ... ; // Name of the entry inside the zip file.
new ZipEntryByteSource(Resources.asByteSource(url), entryName).toCharSource(charset).readLines();
As Olivier Grégoire said, you can create the necessary ByteSources for whatever compression scheme you need in order to use Guava's readLines function.
For zip archives though, although it's possible to do it, I don't think it's worth it. It will be easier to make your own readLines method that iterates over the zip entries and reads the lines of each entry on your own. Here's a class that demonstrates how to read and output the lines of a URL pointing at a zip archive:
public class ReadLinesOfZippedUrl {
public static List<String> readLines(String urlStr, Charset charset) {
List<String> retVal = new LinkedList<>();
try (ZipInputStream zipInputStream = new ZipInputStream(new URL(urlStr).openStream())) {
for (ZipEntry zipEntry = zipInputStream.getNextEntry(); zipEntry != null; zipEntry = zipInputStream.getNextEntry()) {
// don't close this reader or you'll close the underlying zip stream
BufferedReader reader = new BufferedReader(new InputStreamReader(zipInputStream, charset));
retVal.addAll(reader.lines().collect(Collectors.toList())); // slurp all the lines from one entry
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return retVal;
}
public static void main(String[] args) {
String urlStr = "http://central.maven.org/maven2/com/google/guava/guava/18.0/guava-18.0-sources.jar";
Charset charset = StandardCharsets.UTF_8;
List<String> lines = readLines(urlStr, charset);
lines.forEach(System.out::println);
}
}
I am running Mac OSX Mavericks. Right now I am creating a JAR file from a folder (org, the package). When I use this code from here:
public void run() throws IOException
{
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream("/Users/username/Library/Application Support/VSE/temp/output.jar"), manifest);
add(new File("/Users/username/Library/Application Support/VSE/temp/org"), target);
target.close();
}
private void add(File source, JarOutputStream target) throws IOException
{
BufferedInputStream in = null;
try
{
if (source.isDirectory())
{
String name = source.getPath().replace("\\", "/");
if (!name.isEmpty())
{
if (!name.endsWith("/"))
name += "/";
JarEntry entry = new JarEntry(name);
entry.setTime(source.lastModified());
target.putNextEntry(entry);
target.closeEntry();
}
for (File nestedFile: source.listFiles())
add(nestedFile, target);
return;
}
JarEntry entry = new JarEntry(source.getPath().replace("\\", "/"));
entry.setTime(source.lastModified());
target.putNextEntry(entry);
in = new BufferedInputStream(new FileInputStream(source));
byte[] buffer = new byte[1024];
while (true)
{
int count = in.read(buffer);
if (count == -1)
break;
target.write(buffer, 0, count);
}
target.closeEntry();
}
finally
{
if (in != null)
in.close();
}
}
When I extract the JAR file, There is a META-INF folder, but instead of having the org folder in the extracted jar, I have my Users folder copied into it (except because of it's size, its wasn't filled with all my stuff and my application crashed). I'm expecting this is because the code was written for a Windows system, and the differences with the filesystem (such as \ or /). How would I make the code include only the "org" directory, and not everything leading up to it?
Provided you use Java 7+ you may easily do this by using one of my packages in combination with the zip filesystem provider of the JDK to create it:
private static final Map<String, ?> ENV = Collections.singletonMap("create", "true");
public void run()
throws IOException
{
final Path zipPath = Paths.get("/Users/username/Library/Application Support/VSE/temp/output.jar");
final Path srcdir = Paths.get("/Users/username/Library/Application Support/VSE/temp/org");
final URI uri = URI.create("jar:" + zipPath.toUri());
Files.deleteIfExists(zipPath);
try (
final FileSystem zipfs = FileSystems.newFileSystem(uri, ENV);
) {
copyManifest(zipfs);
copyDirectory(srcdir, zipfs);
}
}
private void copyManifest(final FileSystem zipfs)
throws IOException
{
final Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
Files.createDirectory(zipfs.getPath("META-INF/");
try (
final OutputStream out = Files.newOutputStream(zipfs.getPath("META-INF/MANIFEST.MF"));
) {
manifest.write(out);
}
}
private void copyDirectory(final Path srcdir, final FileSystem zipfs)
{
final String lastName = srcdir.getFileName().toString();
final Path dstDir = zipfs.getPath(lastName);
Files.createDirectory(dstDir);
MoreFiles.copyRecursive(srcDir, dstDir, RecursionMode.FAIL_FAST);
}
I'm downloading a zip file from an ftp server. The zip file contains a couple csv files. I'm trying to extract both csv files so that I can pass them into Opencsv, but I seem to be having some issues. I'm assuming there must be a better way to handle this than the way I'm doing it below. How do you return my csv files so that they are available in a list for my csv reader?
My code
ftp.retrieveFile(file, output);
InputStream inputStream = new ByteArrayInputStream(output.toByteArray());
Map<String, InputStream> inputStreams = new HashMap<>();
if (importTask.isZipfile()) {
inputStreams.put("products", importUtils.getZipData(new ZipInputStream(inputStream), importTask.getFilename()));
if(importTask.getCustomerFilename() != null) {
inputStream = new ByteArrayInputStream(output.toByteArray());
inputStreams.put("customers", importUtils.getZipData(new ZipInputStream(inputStream), importTask.getCustomerFilename()));
}
} else {
inputStreams.put("products", inputStream);
}
ftp.logout();
ftp.disconnect();
return inputStreams;
Zip
public InputStream getZipData(ZipInputStream zip, String filename) throws FileNotFoundException, IOException {
for (ZipEntry e; (e = zip.getNextEntry()) != null;) {
if (e.getName().equals(filename)) {
return zip;
}
}
throw new FileNotFoundException("zip://" + filename);
}
If you use Java 7+ you have an easier solution than that; you can just use the zip filesystem provider.
Here is some sample code; note that you need to .close() the generated InputStreams and FileSystems:
public static void getFsFromZipFile(final Path zipFile)
throws IOException
{
final URI uri = URI.create("jar:" + zipFile.toUri());
final Map<String, ?> env = Collections.singletonMap("readonly", "true");
return FileSystems.newFileSystem(uri, env);
}
public static getInputStreamFromZip(final FileSystem zipfs, final String name)
throws IOException
{
return Files.newInputStream(zipfs.getPath(name));
}
This is not how I'd recommend you do it however. What I'd recommend is this:
final Map<String, Path> getFilesFromZip(final Path zipFile, final String... names)
throws IOException
{
Path tmpfile;
final URI uri = URI.create("jar:" + zipFile.toUri());
final Map<String, ?> env = Collections.singletonMap("readonly", "true);
final Map<String, Path> ret = new HashMap<>();
try (
final FileSystem zipfs = FileSystems.newFileSystem(uri, env);
) {
for (final String name: names) {
tmpfile = Files.createTempFile("tmp", ".csv");
Files.copy(zipfs.getPath(name), tmpfile);
ret.put(name, tmpfile);
}
return ret;
}
}