Read large file using vertx - vert.x

I am new to using vertx and I am using vertx filesystem api to read file of large size.
vertx.fileSystem().readFile("target/classes/readme.txt", result -> {
if (result.succeeded()) {
System.out.println(result.result());
} else {
System.err.println("Oh oh ..." + result.cause());
}
});
But the RAM is all consumed while reading and the resource is not even flushed after use. The vertx filesystem api also suggest
Do not use this method to read very large files or you risk running out of available RAM.
Is there any alternative to this?

To read large file you should open an AsyncFile:
OpenOptions options = new OpenOptions();
fileSystem.open("myfile.txt", options, res -> {
if (res.succeeded()) {
AsyncFile file = res.result();
} else {
// Something went wrong!
}
});
Then an AsyncFile is a ReadStream so you can use it together with a Pump to copy the bits to a WriteStream:
Pump.pump(file, output).start();
file.endHandler((r) -> {
System.out.println("Copy done");
});
There are different kind of WriteStream, like AsyncFile, net sockets, HTTP server responses, ...etc.

To read/process a large file in chunks you need to use the open() method which will return an AsyncFile on success. On this AsyncFile you setReadBufferSize() (or not, the default is 8192), and attach a handler() which will be passed a Buffer of at most the size of the read buffer you just set.
In the example below I have also attached an endHandler() to print a final newline to stay in line with the sample code you provided in the question:
vertx.fileSystem().open("target/classes/readme.txt", new OpenOptions().setWrite(false).setCreate(false), result -> {
if (result.succeeded()) {
result.result().setReadBufferSize(READ_BUFFER_SIZE).handler(data -> System.out.print(data.toString()))
.endHandler(v -> System.out.println());
} else {
System.err.println("Oh oh ..." + result.cause());
}
});
You need to define READ_BUFFER_SIZE somewhere of course.

The reason for that is that internally .readFile calls to Files.readAllBytes.
What you should do instead is create a stream out of your file, and pass it to Vertx handler:
try (InputStream steam = new FileInputStream("target/classes/readme.txt")) {
// Your handling here
}

Related

Why 'link' variable gets changed to null even after i assign it a value

private fun shareOperation(file: File) {
val uri = Uri.fromFile(file)
val storage = FirebaseStorage.getInstance()
val pdfRef = storage.reference.child("pdf/${uri.lastPathSegment}")
pdfRef.putFile(uri).addOnFailureListener { e ->
Log.e(TAG, "Couldn't share " + e.message)
}.addOnCompleteListener{
it.addOnCompleteListener {
pdfRef.downloadUrl.addOnSuccessListener { e ->
run {
link = e.toString()
Log.i(TAG,link!!) // Here i get the link to file in firebase storage
}
}
}
}
// Here link gets null
}
i was expecting somehow i can get the link to the file and can use it for sharing intent
You are performing an asynchronous call to upload the file, that is correct since any UI blocking action must be performed in background. The variable link will be null until the run code is executed in the background thread.
You need to code inside the run block whatever you want to happen when the link is available.
BTW looks weird what you are doing with the nested addOnCompleteListener, there should be an easier way to code that. You should probably spend time learning how to code with listeners and background threads.

How to use sendFile method for sending the file located on internet?

I want to use Vert.x routingContext.response().sendFile method to read the file from internet and send it to some handler.
I have tried to use routingContext.response().sendFile for files located on my local system which works fine but instead of local system file when I am using file located on internet, I am getting error java.io.FileNotFoundException
String filename = "http://www.awitness.org/prophecy.zip";
routingContext.response().sendFile(filename, asr->{
if(asr.succeeded()) {
System.out.println("success....");
} else {
System.out.println("Something went wrong " + asr.cause());
}
});
Getting this output:
Something went wrong java.io.FileNotFoundException
That's because sendFile() takes local file path as argument.
Best solution would be to download this file, and serve it from your application.
Worse solution is to download this file on demand, save it using vertx.fileSystem().createTempFile(), and still serve it locally.
Now, for the sake of the argument, let's decided that you would like to go down the second path. How would you do that? You can try something like this:
final Vertx vertx = Vertx.vertx();
final Router router = Router.router(vertx);
WebClient c = WebClient.create(vertx);
String temp = vertx.fileSystem().createTempFileBlocking("", "");
c.get("www.awitness.org", "/prophecy.zip").send(r -> {
if (r.succeeded()) {
Buffer buffer = r.result().body();
vertx.fileSystem().writeFileBlocking(temp, buffer);
}
});
router.route("/").produces("application/zip").handler(ctx -> {
ctx.response().sendFile(temp);
});
I'm using blocking APIs only for the sake of simplicity. Correct ones are the async ones.

Vert.x Write Buffer to WriteStream?

At a Vert.x verticle I'm implementing I have a Buffer that was previously loaded into memory and now I want to dump it into disk.
As far as I understood we should use a Pump to make sure not to overload the WriteStream.
But I'm not finding a way to get a ReadStream child instance from a Buffer. Shouldn't there be an easy / standard way to do this?
Regards
Generally, vert.x does not warn on any issues writing directly into AsyncFiles. Furthermore, they provide the corresponding example of using AsyncFile.write directly here and state that you can use those to write directly: http://vertx.io/docs/vertx-core/java/#_asynchronous_files
However, if you want the pump with Buffer you need an instance of ReadStream<Buffer> along with an AsyncFile to pump into. You can make use of the implementation by PitchPoint Solutions (Copyright 2016 The Simple File Server Authors):
https://github.com/pitchpoint-solutions/sfs/blob/master/sfs-server/src/main/java/org/sfs/io/BufferReadStream.java
Putting it all together:
CompletableFuture<Void> done = new CompletableFuture<>();
Buffer buffer = Buffer.buffer(new byte[100]);
Vertx.vertx().fileSystem.open("myfile.txt", new OpenOptions(), res -> {
if (res.succeeded()) {
AsyncFile outputFile = res.result();
BufferReadStream reader = new BufferReadStream(buffer)
Pump pump = Pump.pump(reader, outputFile);
pump.start();
reader.endHandler((r) -> {
pump.stop(); // not sure this is required
done.complete(null);
});
} else {
// Something went wrong!
}
});
// wait elsewhere
done.get();

Vertx merge contents of multiple files in single file

What is the best way to append the contents of multiple files into single file in vertx? I have tried vertx filesystem and asyncFile but both do not have a option to append file or I did not know of any. Is there any alternative approach to merge or append files in vertx asynchronously.
The only solution I could find is to make buffer list and write content on the end of each previous buffer length using loop.
Indeed, as of Vert.x 3.4, there is no helper method on FileSystem to append a file to another file.
You could do it with AsyncFile and Pump as follows.
First create a utility method to open files:
Future<AsyncFile> openFile(FileSystem fileSystem, String path, OpenOptions openOptions) {
Future<AsyncFile> future = Future.future();
fileSystem.open(path, openOptions, future);
return future;
}
Then another one to append a file to another file with a Pump:
Future<AsyncFile> append(AsyncFile source, AsyncFile destination) {
Future<AsyncFile> future = Future.future();
Pump pump = Pump.pump(source, destination);
source.exceptionHandler(future::fail);
destination.exceptionHandler(future::fail);
source.endHandler(v -> future.complete(destination));
pump.start();
return future;
}
Now you can combine those with sequential composition:
void merge(FileSystem fileSystem, String output, List<String> sources) {
openFile(fileSystem, output, new OpenOptions().setCreate(true).setTruncateExisting(true).setWrite(true)).compose(outFile -> {
Future<AsyncFile> mergeFuture = null;
for (String source : sources) {
if (mergeFuture == null) {
mergeFuture = openFile(fileSystem, source, new OpenOptions()).compose(sourceFile -> {
return append(sourceFile, outFile);
});
} else {
mergeFuture = mergeFuture.compose(v -> {
return openFile(fileSystem, source, new OpenOptions()).compose(sourceFile -> {
return append(sourceFile, outFile);
});
});
}
}
return mergeFuture;
}).setHandler(ar -> {
System.out.println("Done");
});
}

Threading concept

Can somebody help me on this:
private Thread workerThread;
private EventWaitHandle waitHandle;
if (workerThread == null)
{
workerThread = new Thread(new ThreadStart(Work));
workerThread.Start();
//workerThread.Join();
}
else if (workerThread.ThreadState == ThreadState.WaitSleepJoin)
{
waitHandle.Set();
}
private void Work()
{
while (true)
{
string filepath = RetrieveFile();
if (filepath != null)
ProcessFile(filepath);
else
// If no files left to process then wait
waitHandle.WaitOne();
}
}
private void ProcessFile(string filepath)
{
XMLCreation myXML = new XMLCreation();
myXML.WriteXml(filepath, XMLFullFilePath);
}
private string RetrieveFile()
{
if (workQueue.Count > 0)
return workQueue.Dequeue();
else
return null;
}
see this is how all this work.
i have a filewatcher event that fires only when new file is being add to that folder, now the problem is its a small part of bigger application and when the file watcher fires there is another process which is accessing that file and i get error like this file is being used by another process. so i have tried to implement through threading but with the above code only some files are being processed, but in the log i can see all the files are being processed. Is it the right way to do it or am i missing something in it
thanks in adv.
You will have to use a mutex to control who is accessing the file and allow only one process at a time to work with that file at the very first time. If you think that there is the possibility that more than one thread will be waiting to work with the same file then you will have to implement a producer-consumer threading system with a queue.
Here is the best documentation about threads you can find in .NET:
http://www.albahari.com/threading/