Apache Camel: Download Multiple Files at once using SFTP component - java

on sftp i have several files with following xyz names:
40_20200313_0cd6963f-bf5b-4eb0-b310-255a23ed778e_p.dat
123_20200313_0cd6963f-bf5b-4eb0-b310-255a23ed778e_p.dat
etc.
I want camel to download all files at once as currently it is downloading file one by one.
Following is camel route and query:
private static String regex() {
return "(22|23|24|25|26|28|29|32|35|40|41|46|52|70|85|88|123)_(?:.*)_p.dat";
}
private static String sftpComponent() {
return "sftp://transit.ergogroup.no/Eyeshare/From_Eyeshare_Test"
+ "?username=Eyeshare_test"
+ "&password=epw3ePOugG" // Stored on wildfly server
+ "&download=true" //Shall be read chunk by chunk to avoid heap space issues. Earlier download=true was used: Harpreet
+ "&useList=true"
+ "&stepwise=false"
+ "&disconnect=true"
+ "&passiveMode=true"
+ "&reconnectDelay=10000"
// + "&bridgeErrorHandler=true"
+ "&delay=300000"
//+ "&fileName=" + sftpFileName
// + "&include=kiki\\.txt"
// + "&include=40_*_p\\.dat"sss
+ "&include="+regex()
+ "&preMove=$simple{file:onlyname}.$simple{date:now:yyyy-MM-dd'T'hh-mm-ss}.processing"
+ "&move=$simple{file:onlyname.noext}.$simple{date:now:yyyy-MM-dd'T'hh-mm-ss}.success"
+ "&moveFailed=$simple{file:onlyname.noext}.$simple{date:now:yyyy-MM-dd'T'hh-mm-ss}.failed";
// + "&idempotentRepository=#infinispan"
// + "&readLockRemoveOnCommit=true";
}
from(sftpComponent()).log("CHU").to(archiveReceivedFile())
Code appears fine but output is not. Anyone kindly suggest

Here some example of aggregator:
from("file:///somePath/consume/?maxMessagesPerPoll=2&delay=5000")
.aggregate(constant(true), new ZipAggregationStrategy()).completion(exchange -> exchange.getProperty("CamelBatchComplete", Boolean.class))
.to("file:///somePath/produce/")
Here maxMessagesPerPoll defining how many files will be archived. But if number of them in folder is lower then maxMessagesPerPoll value it will wait for missing files for complete archive. Here example of ZipAggregationStrategy:
private static class ZipAggregationStrategy implements AggregationStrategy {
private ZipOutputStream zipOutputStream;
private ByteArrayOutputStream out;
#Override
public Exchange aggregate(final Exchange oldExchange, final Exchange newExchange) {
try {
if (oldExchange == null) {
out = new ByteArrayOutputStream();
zipOutputStream = new ZipOutputStream(out);
}
createEntry(newExchange);
return newExchange;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void createEntry(final Exchange exchange) throws Exception {
final ZipEntry zipEntry = new ZipEntry(exchange.getIn().getHeader(Exchange.FILE_NAME, String.class));
zipOutputStream.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
try (InputStream body = exchange.getIn().getBody(InputStream.class)) {
while ((length = body.read(bytes)) >= 0) {
zipOutputStream.write(bytes, 0, length);
}
}
}
#Override
public void onCompletion(final Exchange exchange) {
try {
zipOutputStream.close();
exchange.getIn().setBody(new ByteArrayInputStream(out.toByteArray()));
exchange.getIn().setHeader(Exchange.FILE_NAME, "someArchive.zip");
}catch (Exception e){
throw new RuntimeException(e);
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
It's in-memory example. You can improve it for example with using temporary file. And you can always create your own completion predicate based on your logic.
UPD: i think link for documentation is temporary unavailable

Related

Why can't my web client accept an image from my web server?

I this is my java HTTP server:
public class WebServer implements Runnable {
public static final int PORT = 80;
#Override
public void run() {
HttpServer $server;
try {
$server = HttpServer.create(new InetSocketAddress(80), 0);
} catch (IOException _e) {
throw new RuntimeException(_e);
}
$server.createContext("/", _httpExchange ->
{
String $uri = _httpExchange.getRequestURI().toString();
$uri = $uri.startsWith("/") ? $uri.replaceFirst("/", "") : $uri;
if ($uri.equals("")) {
sendFile("test.html", _httpExchange);
}
else if ($uri.matches(".*\\.[^/.]+")) {
sendFile($uri, _httpExchange);
}
else {
sendFile($uri + ".html", _httpExchange);
}
});
$server.start();
System.out.println("Server started at " + getPrivateIp() + " on port " + PORT);
}
private static String getPrivateIp() {
try (final DatagramSocket datagramSocket = new DatagramSocket()) {
datagramSocket.connect(InetAddress.getByName("8.8.8.8"), 12345);
return datagramSocket.getLocalAddress().getHostAddress();
} catch (UnknownHostException | SocketException _e) {
throw new RuntimeException(_e);
}
}
public static void sendFile(String _name, HttpExchange _exchange) throws IOException {
try {
InputStream $stream = WebServer.class.getResourceAsStream(_name);
if ($stream == null) {
_exchange.sendResponseHeaders(404, 0);
_exchange.close();
return;
}
Scanner $scanner = new Scanner($stream).useDelimiter("\\A");
String $response = $scanner.next();
_exchange.getResponseBody();
_exchange.sendResponseHeaders(200, $response.getBytes().length);
_exchange.getResponseBody().write($response.getBytes());
_exchange.close();
} catch (Exception _ex) {
throw new RuntimeException(_ex);
}
}
}
When I run it, and then open my website, everything is ok, but I cannot see any images. In the network tab, it says that the image was accepted, but it's not shown. I tried using Files.copy() in sendFile() method, but it didn't work - it didn't show the website, nor the image! (Not even when I did localhost/image.jpg).
In the network tab, it also shows that the MIME type is img/jpeg, which is correct, so it's not because of that...
Using wget, I get a normal looking .jpg file, but if I open it, it's corrupted...
Does someone know how to fix this?
Thanks.
Solved it!
You just check if the request wants .png or .jpg file (or you can just check the MIME type), and if it does, then you have to use ImageIO class
public static void sendFile(String _name, HttpExchange _exchange) {
try {
InputStream $stream = WebServer.class.getResourceAsStream(_name);
if ($stream == null) {
_exchange.sendResponseHeaders(404, 0);
_exchange.close();
return;
}
if (_name.matches(".*?\\.(png|PNG|jpg|JPG|jpeg|JPEG)")) {
BufferedImage $image = ImageIO.read($stream);
if (_name.toLowerCase().endsWith("png")) {
_exchange.sendResponseHeaders(200, getImageSize($image, "png"));
ImageIO.write($image, "png", _exchange.getResponseBody());
}
else {
_exchange.sendResponseHeaders(200, getImageSize($image,"jpeg"));
ImageIO.write($image, "jpeg", _exchange.getResponseBody());
}
$stream.close();
_exchange.close();
return;
}
Scanner $scanner = new Scanner($stream).useDelimiter("$");
String $response = $scanner.next();
_exchange.getResponseBody();
_exchange.sendResponseHeaders(200, $response.length());
_exchange.getResponseBody().write($response.getBytes());
_exchange.close();
} catch (Exception _ex) {
throw new RuntimeException(_ex);
}
}

Monitoring Zip4J extractAll() method progress monitor

I am using Zip4J for extracting zip file and I am able to do it. However, I want to use progress monitor provided in Zip4J but not able to use it successfully.
The documentation only says that it should have run in thread mode true. I did it and my console stuck on this on command line. Any working example of extractAll() with progress monitor.
public String unzipFile(String sourceFilePath, String extractionPath) {
String extractionDirectory = "";
FileHeader fileHeader = null;
if (FileUtility.isPathExist(sourceFilePath) && FileUtility.isPathExist(extractionPath)) {
try {
ZipFile zipFile = new ZipFile(sourceFilePath);
LOG.info("File Extraction started");
List<FileHeader> fileHeaderList = zipFile.getFileHeaders();
if (fileHeaderList.size() > 0)
fileHeader = (FileHeader) fileHeaderList.get(0);
if (fileHeader != null)
extractionDirectory = splitFileName(fileHeader.getFileName());
long totalPercentage = 235;
long startTime = System.currentTimeMillis();
zipFile.extractAll(extractionPath);
LOG.info("File Extraction completed.");
System.out.println();
} catch (ZipException e) {
LOG.error("Extraction Exception ->\n" + e.getMessage());
}
} else {
LOG.error("Either source path or extraction path is not exist.");
}
return extractionDirectory;
}
Don't know, works fine if you add enough files, that there actually is a progress to see. I added some really fat ones for the purpose.
#Test
public void testExtractAllDeflateAndNoEncryptionExtractsSuccessfully() throws IOException {
ZipFile zipFile = new ZipFile(generatedZipFile);
List<File> toAdd = Arrays.asList(
getTestFileFromResources("sample_text1.txt"),
getTestFileFromResources("sample_text_large.txt"),
getTestFileFromResources("OrccTutorial.pdf"),
getTestFileFromResources("introduction-to-automata-theory.pdf"),
getTestFileFromResources("thomas.pdf")
);
zipFile.addFiles(toAdd);
zipFile.setRunInThread(true);
zipFile.extractAll(outputFolder.getPath());
ProgressMonitor mon = zipFile.getProgressMonitor();
while (mon.getState() == BUSY) {
System.out.println(zipFile.getProgressMonitor().getPercentDone());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder);
verifyNumberOfFilesInOutputFolder(outputFolder, 5);
}
testAddFilesWithProgressMonitor.java in the the project's test cases shows how to use ProgressMonitor.

I want to get one line as header and rest data is append in file

I want to get one line as header and then rest of data append in the file.but i am facing issue that it is saving the header repeatedly when i have called the function.
Expected output should be like
Id : Title : Group ID
1 : ab : 2
2 : fd : 3
3 : fwsj : 3
public void writeOutputToFile(int id, String title, int groupId) throws IOException {
OutputStream os = new FileOutputStream(new File("output_report.txt"), true);
os.write("\n Id Title Group ID \n ".getBytes());
os.write((id + " " +title + " " + groupId + "\n").getBytes());
os.close();
}
well, inside your method you write the headers to the file, so obviously whenever you call it they'll get written..
You can separate it to two methods- one that writes the headers (and called only once) and another that writes the data (and called once per row).
Alternatively, use some sort of loop inside your method to write each of the lines to the file, after writing the headers once.
The Problem
It is repeatedly putting in the header, because when you call the method, you are always going to insert the header. Instead, you may want to code a util that inputs headers for a file you are creating, and then a separate method for inserting the data.
The Solution
Solution 1)
The helper util method would look something like this:
// String... allows for multiple string parameters to be entered for all of your headers.
public void prepFile(File f, String... headers) {
StringBuffer buffer = new StringBuffer();
for (String header : headers) {
buffer.append(header + "\t");
}
OutputStream os = new FileOutputStream(f, true);
os.write(buffer.toString().getBytes());
os.close();
}
After the file is prepped, you can then use your writeOutputToFile method for all the data.
Edit
Solution 2)
If you were going to make a stand alone class for this, I would recommend you set it up like so:
import java.io.*;
public class OutputFile {
private File file;
private String[] headers;
private boolean existed;
public OutputFile(File f, String... headers) {
this.file = f;
this.headers = headers;
init();
}
private void init() {
existed = file.exists();
// If the file didn't exist, then you want to create it.
if (!existed) {
try {
file.createNewFile();
// Afterwards, you can then write your headers to it.
if (headers != null) {
writeData(headers);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void writeData(int id, String title, int groupId) {
writeData("" + id, title, "" + groupId);
}
public void writeData(String... strings) {
StringBuffer buffer = new StringBuffer();
for (String s : strings) {
buffer.append(s + "\t");
}
buffer.append("\n");
writeData(buffer.toString());
}
public void writeData(String data) {
OutputStream os = null;
try {
os = new FileOutputStream(file, true);
os.write(data.getBytes());
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

How to fix incomplete download from netty server

I'm implementing some simple netty server and client to send and revieve files. Something which is similar to cloud storage.
I have a server which handles incoming requests and sends the files back to the client. I also want my apps to be able to handle with big files, that's why I divide such files into chunks and send them chunk by chunk. But there's an issue I can't resolve.
Let's say:
We have a 4 gb file on a server.
It is divided into 40 000 chunks.
Then they are sent to a client application, and I can see that all the chunks at the server are written into socket, as I use int field as a message number (chunk number) and put into log a message number which is being written.
But then when a client receives messages (chunks), in the case of large files the process doesn't finish successfully and only some (it depends on the size of a file) of the chunks are received by a client.
A client starts receiving consecutive messages - 1, 2, 3, 4 ... 27878, 27879 and then stops with no exception, although the last message from server was, for example 40000.
Almost forgot to say that I use JavaFX for the client app.
So, I tried to play with xms xmx java vm options but it didn't help.
Server
public class Server {
public void run() throws Exception {
EventLoopGroup mainGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(mainGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(
new ObjectDecoder(Constants.FRAME_SIZE, ClassResolvers.cacheDisabled(null)),
new ObjectEncoder(),
new MainHandler()
);
}
})
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture future = b.bind(8189).sync();
future.channel().closeFuture().sync();
} finally {
mainGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new Server().run();
}
}
Server handler
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
if (msg == null) {
return;
}
if (msg instanceof FileRequest) {
FileRequest fr = (FileRequest) msg;
switch (fr.getFileCommand()) {
case DOWNLOAD:
sendFileToClient(ctx, fr.getFilename());
break;
case LIST_FILES:
listFiles(ctx);
break;
case DELETE:
deleteFileOnServer(fr);
listFiles(ctx);
break;
case SEND:
saveFileOnServer(fr);
listFiles(ctx);
break;
case SEND_PARTIAL_DATA:
savePartialDataOnServer(fr);
break;
}
}
} finally {
ReferenceCountUtil.release(msg);
}
}
Methods for sending files in chunks
private void sendFileToClient(ChannelHandlerContext ctx, String fileName) throws IOException {
Path path = Paths.get("server_storage/" + fileName);
if (Files.exists(path)) {
if (Files.size(path) > Constants.FRAME_SIZE) {
sendServerDataFrames(ctx, path);
ctx.writeAndFlush(new FileRequest(FileCommand.LIST_FILES));
} else {
FileMessage fm = new FileMessage(path);
ctx.writeAndFlush(fm);
}
}
}
private void sendServerDataFrames(ChannelHandlerContext ctx, Path path) throws IOException {
byte[] byteBuf = new byte[Constants.FRAME_CHUNK_SIZE];
FileMessage fileMessage = new FileMessage(path, byteBuf, 1);
FileRequest fileRequest = new FileRequest(FileCommand.SEND_PARTIAL_DATA, fileMessage);
FileInputStream fis = new FileInputStream(path.toFile());
int read;
while ((read = fis.read(byteBuf)) > 0) {
if (read < Constants.FRAME_CHUNK_SIZE) {
byteBuf = Arrays.copyOf(byteBuf, read);
fileMessage.setData(byteBuf);
}
ctx.writeAndFlush(fileRequest);
fileMessage.setMessageNumber(fileMessage.getMessageNumber() + 1);
}
System.out.println("server_storage/" + path.getFileName() + ", server last frame number: " + fileMessage.getMessageNumber());
System.out.println("server_storage/" + path.getFileName() + ": closing file stream.");
fis.close();
}
client handlers
#Override
public void initialize(URL location, ResourceBundle resources) {
Network.start();
Thread t = new Thread(() -> {
try {
while (true) {
AbstractMessage am = Network.readObject();
if (am instanceof FileMessage) {
FileMessage fm = (FileMessage) am;
Files.write(Paths.get("client_storage/" + fm.getFilename()), fm.getData(), StandardOpenOption.CREATE);
refreshLocalFilesList();
}
if (am instanceof FilesListMessage) {
FilesListMessage flm = (FilesListMessage) am;
refreshServerFilesList(flm.getFilesList());
}
if (am instanceof FileRequest) {
FileRequest fr = (FileRequest) am;
switch (fr.getFileCommand()) {
case DELETE:
deleteFile(fr.getFilename());
break;
case SEND_PARTIAL_DATA:
receiveFrames(fr);
break;
case LIST_FILES:
refreshLocalFilesList();
break;
}
}
}
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
} finally {
Network.stop();
}
});
t.setDaemon(true);
t.start();
refreshLocalFilesList();
Network.sendMsg(new FileRequest(FileCommand.LIST_FILES));
}
private void receiveFrames(FileRequest fm) throws IOException {
Utils.processBytes(fm.getFileMessage(), "client_storage/");
}
public final class Utils {
public static void processBytes(FileMessage fm, String pathPart) {
Path path = Paths.get(pathPart + fm.getFilename());
byte[] data = fm.getData();
System.out.println(pathPart + path.getFileName() + ": " + fm.getMessageNumber());
try {
if (fm.getMessageNumber() == 1) {
Files.write(path, data, StandardOpenOption.CREATE_NEW);
} else {
Files.write(path, data, StandardOpenOption.WRITE, StandardOpenOption.APPEND);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
That what I see on server.
server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 42151
server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 42152
server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso, server last frame number: 42153
server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: closing file stream.
And this one is on a client.
client_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 29055
client_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 29056
client_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 29057
And there is no issue when sending files from the client to the the server. I can see in debugger and in the windows task manager that both processes are working simultaniously but it's not like this when a file is sent from the server to the client. First all the chunks are read and then they are sent to a client and it starts to receive them but failed to get all of them.
Please help. I have no idea what it could be. Thanks in advance.

Spigot-Bungeecord Plugin Messaging not working

I am trying to make a plugin that has a 'global' configuration file. Right now, I'm trying to use Plugin Messaging to send the entire configuration file through a string, to another server. I have followed the guide at https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/ and have put my own little twist on what is sent. I'm trying to send the plugin message within a spigot plugin so maybe that is the problem. Here is the code is a summary of the code I use to send it (I took out readFile(), clearFile() and writeFile(), let me know if you want those):
public class Main extends JavaPlugin implements PluginMessageListener {
public void onEnable() {
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", this);
}
public void onDisable() {}
public void updateConfig() {
String updateConfig = "";
for (String s : readFile(this.getDataFolder() + "/config.yml")) {
if (updateConfig.equals("")) {
updateConfig = s;
} else {
updateConfig = updateConfig + " |n| " + s;
}
}
Bukkit.getLogger().info("Sending config update...");
sendUpdateconfig(updateConfig);
}
public void sendUpdateconfig(String update) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
try {
out.writeUTF("Forward");
out.writeUTF("ALL");
out.writeUTF("FooServer");
ByteArrayOutputStream msgbytes = new ByteArrayOutputStream();
DataOutputStream msgout = new DataOutputStream(msgbytes);
msgout.writeUTF(update);
msgout.writeShort(295);
out.writeShort(msgbytes.toByteArray().length);
out.write(msgbytes.toByteArray());
Player player = Iterables.getLast(Bukkit.getOnlinePlayers());
player.getServer().sendPluginMessage(this, "BungeeCord", out.toByteArray());
Bukkit.getLogger().info("Sent " + update);
Bukkit.getLogger().info("Short sent: 295");
Bukkit.getLogger().info("Sent through player " + player.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
Bukkit.getLogger().info("Recieved message...");
if (!channel.equals("BungeeCord")) {
return;
}
try {
Bukkit.getLogger().info("Recieved message...");
ByteArrayDataInput in = ByteStreams.newDataInput(message);
String subChannel = in.readUTF();
if (!subChannel.equals("FooServer")) {
Bukkit.getLogger().info("Loading message....");
short len = in.readShort();
byte[] msgbytes = new byte[len];
in.readFully(msgbytes);
DataInputStream msgin = new DataInputStream(new ByteArrayInputStream(msgbytes));
String somedata = msgin.readUTF();
short somenumber = msgin.readShort();
if (somenumber == 295) {
Bukkit.getLogger().info("Updating config...");
String[] toWrite = somedata.split(" |n| ");
String path = (this.getDataFolder() + "/config.yml");
clearFile(path);
for (String s : toWrite) {
writeFile(path, s);
}
Bukkit.getLogger().info("Config updated!");
}
} else {
Bukkit.getLogger().info("Message sent by this plugin.");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
The way I send the message is just by calling, updateConfig(); When that is called, onPluginMessageReceived is never run.
Is there something I'm doing wrong? Can plugin messages only be sent by a bungeecord plugin? Thanks in advance. If you have any questions about the code, let me know.
Don't work beacause it's write ( String server to send to, or ALL to send to every server (except the one sending the plugin message)) ! For use it you can use our own channel or redis

Categories