Je dois obtenir le contenu du fichier à partir de l'archive Zip (un seul fichier, je connais son nom) à l'aide de SFTP. La seule chose que j'ai est le InputStream
de Zip. La plupart des exemples montrent comment obtenir du contenu à l'aide de cette déclaration:
ZipFile zipFile = new ZipFile("location");
Mais comme je l'ai dit, je n'ai pas de fichier Zip sur ma machine locale et je ne veux pas le télécharger. Un InputStream
suffit-il pour lire?
PD: Voici comment je fais:
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();
}
}
}
Eh bien, je l'ai fait:
zipStream = new ZipInputStream(channelSftp.get("Port_Increment_201405261400_2251.Zip"));
zipStream.getNextEntry();
sc = new Scanner(zipStream);
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
Cela m'aide à lire le contenu de Zip sans écrire dans un autre fichier.
Voici un exemple simple sur la façon d'extraire un fichier Zip, vous devrez vérifier si le fichier est un répertoire. Mais c'est le plus simple.
L'étape qui vous manque est la lecture du flux d'entrée et l'écriture du contenu dans un tampon qui est écrit dans un flux de sortie.
// 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();
}
}
L'extrait de code provient du site suivant:
ZipInputStream
est un InputStream
en lui-même et délivre le contenu de chaque entrée après chaque appel à getNextEntry()
. Une attention particulière doit être prise, de ne pas fermer le flux à partir duquel le contenu est lu, car il est le même que le flux 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.");
}
}
Lors de la délégation du contenu de lecture à une autre logique, il peut être nécessaire d'envelopper le ZipInputStream
avec un FilterInputStream
pour fermer uniquement l'entrée au lieu du flux entier comme dans:
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();
}
});
}
}
OP était proche. Il suffit de lire les octets. L'appel à getNextEntry positions the stream at the beginning of the entry data
( documents ). Si c'est l'entrée que nous voulons (ou la seule entrée), alors le InputStream est au bon endroit. Tout ce que nous devons faire est de lire les octets décompressés de cette entrée.
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);
}
Donc, si ces octets sont vraiment du texte, nous pouvons décoder ces octets en une chaîne. Je suppose simplement le codage utf8.
new String(bytes, "utf8")
Note latérale: J'utilise personnellement Apache commons-io IOUtils pour réduire ce genre de choses de niveau inférieur. Les documents pour ZipInputStream.read semblent impliquer que la lecture s'arrêtera à la fin de l'entrée Zip actuelle. Si cela est vrai, alors la lecture de l'entrée textuelle actuelle est une ligne avec IOUtils.
String text = IOUtils.toString(zipStream)
Voici une solution plus générique pour traiter un flux d'entrée Zip avec un BiConsumer. C'est presque la même solution que celle utilisée par 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();
}
});
}
}
}
Vous pouvez l'utiliser en appelant simplement
readZip(<some inputstream>, (entry, is) -> {
/* don't forget to close this stream after processing. */
is.read() // ... <- to read each entry
});