web-dev-qa-db-ja.com

InputStreamを使用してZipからファイルを読み取る方法

SFTPを使用して、Zipアーカイブからファイルコンテンツを取得する必要があります(1つのファイルのみ、その名前は知っています)。私が持っている唯一のものは、ZipのInputStreamです。ほとんどの例は、このステートメントを使用してコンテンツを取得する方法を示しています。

ZipFile zipFile = new ZipFile("location");

しかし、私が言ったように、ローカルマシンにZipファイルがないため、ダウンロードしたくありません。 InputStreamで十分ですか?

PD:これは私が行う方法です:

import Java.util.Zip.ZipInputStream;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

public class SFTP {


    public static void main(String[] args) {

        String SFTPHOST = "Host";
        int SFTPPORT = 3232;
        String SFTPUSER = "user";
        String SFTPPASS = "mypass";
        String SFTPWORKINGDIR = "/dir/work";
        Session session = null;
        Channel channel = null;
        ChannelSftp channelSftp = null;
        try {
            JSch jsch = new JSch();
            session = jsch.getSession(SFTPUSER, SFTPHOST, SFTPPORT);
            session.setPassword(SFTPPASS);
            Java.util.Properties config = new Java.util.Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            session.connect();
            channel = session.openChannel("sftp");
            channel.connect();
            channelSftp = (ChannelSftp) channel;
            channelSftp.cd(SFTPWORKINGDIR);
            ZipInputStream stream = new ZipInputStream(channelSftp.get("file.Zip"));
            ZipEntry entry = zipStream.getNextEntry();
            System.out.println(entry.getName); //Yes, I got its name, now I need to get content
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            session.disconnect();
            channelSftp.disconnect();
            channel.disconnect();
        }


    }
}
21
Tony

さて、私はこれをやった:

 zipStream = new ZipInputStream(channelSftp.get("Port_Increment_201405261400_2251.Zip"));
 zipStream.getNextEntry();

 sc = new Scanner(zipStream);
 while (sc.hasNextLine()) {
     System.out.println(sc.nextLine());
 }

別のファイルに書き込むことなく、Zipのコンテンツを読むのに役立ちます。

15
Tony

以下は、Zipファイルを抽出する方法の簡単な例です。ファイルがディレクトリかどうかを確認する必要があります。しかし、これは最も簡単です。

欠落しているステップは、入力ストリームを読み取り、出力ストリームに書き込まれるバッファーに内容を書き込むことです。

// Expands the Zip file passed as argument 1, into the
// directory provided in argument 2
public static void main(String args[]) throws Exception
{
    if(args.length != 2)
    {
        System.err.println("zipreader zipfile outputdir");
        return;
    }

    // create a buffer to improve copy performance later.
    byte[] buffer = new byte[2048];

    // open the Zip file stream
    InputStream theFile = new FileInputStream(args[0]);
    ZipInputStream stream = new ZipInputStream(theFile);
    String outdir = args[1];

    try
    {

        // now iterate through each item in the stream. The get next
        // entry call will return a ZipEntry for each file in the
        // stream
        ZipEntry entry;
        while((entry = stream.getNextEntry())!=null)
        {
            String s = String.format("Entry: %s len %d added %TD",
                            entry.getName(), entry.getSize(),
                            new Date(entry.getTime()));
            System.out.println(s);

            // Once we get the entry from the stream, the stream is
            // positioned read to read the raw data, and we keep
            // reading until read returns 0 or less.
            String outpath = outdir + "/" + entry.getName();
            FileOutputStream output = null;
            try
            {
                output = new FileOutputStream(outpath);
                int len = 0;
                while ((len = stream.read(buffer)) > 0)
                {
                    output.write(buffer, 0, len);
                }
            }
            finally
            {
                // we must always close the output file
                if(output!=null) output.close();
            }
        }
    }
    finally
    {
        // we must always close the Zip file.
        stream.close();
    }
}

コードの抜粋は、次のサイトからのものです。

http://www.thecoderscorner.com/team-blog/Java-and-jvm/12-reading-a-Zip-file-from-Java-using-zipinputstream#.U4RAxYamixR

19
Kenneth Clark

ZipInputStreamはそれ自体がInputStreamであり、getNextEntry()を呼び出すたびに各エントリの内容を配信します。コンテンツが読み取られるストリームを閉じるのではなく、Zipストリームと同じであるため、特別な注意が必要です。

public void readZipStream(InputStream in) throws IOException {
    ZipInputStream zipIn = new ZipInputStream(in);
    ZipEntry entry;
    while ((entry = zipIn.getNextEntry()) != null) {
        System.out.println(entry.getName());
        readContents(zipIn);
        zipIn.closeEntry();
    }
}

private void readContents(InputStream contentsIn) throws IOException {
    byte contents[] = new byte[4096];
    int direct;
    while ((direct = contentsIn.read(contents, 0, contents.length)) >= 0) {
        System.out.println("Read " + direct + "bytes content.");
    }
}

読み取り内容を他のロジックに委任する場合、次のようにストリーム全体ではなくエントリのみを閉じるために、ZipInputStreamFilterInputStreamでラップする必要があります。

public void readZipStream(InputStream in) throws IOException {
    ZipInputStream zipIn = new ZipInputStream(in);
    ZipEntry entry;
    while ((entry = zipIn.getNextEntry()) != null) {
        System.out.println(entry.getName());

        readContents(new FilterInputStream(zipIn) {
            @Override
            public void close() throws IOException {
                zipIn.closeEntry();
            }
        });
    }
}
8
haui

OPは近かった。バイトを読むだけです。 getNextEntry positions the stream at the beginning of the entry datadocs )への呼び出し。それが必要なエントリ(または唯一のエントリ)の場合、InputStreamは適切な場所にあります。必要なのは、そのエントリの圧縮解除されたバイトを読み取ることだけです。

byte[] bytes = new byte[(int) entry.getSize()];
int i = 0;
while (i < bytes.length) {
    // .read doesn't always fill the buffer we give it.
    // Keep calling it until we get all the bytes for this entry.
    i += zipStream.read(bytes, i, bytes.length - i);
}

したがって、これらのバイトが実際にテキストである場合、それらのバイトを文字列にデコードできます。 utf8エンコーディングを想定しています。

new String(bytes, "utf8")

サイドノート:私は個人的にApache commons-io IOUtils を使用して、この種の低レベルのものを削減します。 ZipInputStream.readのドキュメントは、読み取りが現在のZipエントリの終わりで停止することを暗示しているようです。それが当てはまる場合、現在のテキストエントリの読み取りは、IOUtilsを使用した1行です。

String text = IOUtils.toString(zipStream)
0

ここでは、BiConsumerでZip入力ストリームを処理するより一般的なソリューションです。これは、hauiが使用したソリューションとほぼ同じです。

private void readZip(InputStream is, BiConsumer<ZipEntry,InputStream> consumer) throws IOException {
    try (ZipInputStream zipFile = new ZipInputStream(is);) {
        ZipEntry entry;
        while((entry = zipFile.getNextEntry()) != null){
            consumer.accept(entry, new FilterInputStream(zipFile) {
                @Override
                public void close() throws IOException {
                    zipFile.closeEntry();
                }
            });
        }
    }
}

呼び出すだけで使用できます

readZip(<some inputstream>, (entry, is) -> {
    /* don't forget to close this stream after processing. */
    is.read() // ... <- to read each entry
});
0
ThomasCh