I am using JavaFX media player to play an mp3 file using following code
new MediaPlayer(new Media(FileObject.toURI().toString())).play();
However now I have a requirement that I have the mp3 byte data in memory instead of an File Object. The reason is the mp3 file is encrypted and then shipped along with the program. Hence I need to decrypt the mp3 file in memory or input stream.
I could decrypt the mp3 file to an temporary file in temp directory but this would be a performance overhead and the audio content would be insecure.
From the Media Javadoc
Only HTTP, FILE, and JAR URLs are supported. If the provided URL is invalid then an exception will be thrown. If an asynchronous error occurs, the error property will be set. Listen to this property to be notified of any such errors.
I'm not personally familiar with JavaFX, but that would suggest to me that without resorting to nasty hacks, you're not going to be able to read media directly from memory. Normally for this kind of URI only interface I'd suggest registering a custom UrlStreamHandler and a custom protocol that reads from memory. But assuming that JavaDoc is correct, the JavaFX uses it's own resolution so presumably this will not work.
Given this then I suspect the only way to make this work is to provide access to the in-memory MP3 over HTTP. You could do this using Jetty or any similar embeddable servlet container. Something along the following lines:
1) Start Up Jetty as per Quick Start Guide
2) Register a servlet that looks something like below. This servlet will expose your in-memory data:
public class MagicAccessServlet extends HttpServlet {
private static final Map<String, byte[]> mediaMap = new ConcurrentHashMap();
public static String registerMedia(byte[] media) {
String key = UUID.randomUUID().toString();
mediaMap.put(key, media);
return key;
}
public static deregisterMedia(String key) {
mediaMap.remove(key);
}
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
String key = req.get("key");
byte[] media = mediaMap.get(key);
resp.setContentLength(media.length);
resp.getOutputStream().write(media);
}
}
Then you can access from within your application using an http url. E.g. something like
MagicAccessServlet.registerMedia(decodedMp3);
new MediaPlayer(new Media("http://localhost:<port>/<context>/<servlet>?key=" + key)).play();
Unfortunately as the Media constructor stands I see no easy way to do this other than the temporary file approach. Note that while I agree the performance would have an overhead, if the files aren't too big (most mp3 files generally aren't) then the overhead should be minimal in this sense. And technically, decoding the content to memory also renders it insecure (though admittedly much harder to extract.)
One slightly crazy approach I did think of was to use sockets. You could setup a separate part of your application which decrypts the unencrypted content and then streams the raw mp3 bytes over a certain port on localhost. You could then provide this as a HTTP URI to the Media constructor.
Related
The usual problem in Java is that you have to hack to get a proper unmapping of memory mapped files - see here for the 14year old bug report ;)
But on Android there seems to be 0 solutions in pure Java and just via NDK. Is this true? If yes, any pointers to an open source solution with Android/Java bindings?
There is no hack available under Android.
But there are a few helpers and snippets which make the C-Java binding for mmap files easy/easier:
util-mmap, Apache License 2.0, here is an issue regarding Android support
Using Memory Mapped Files and JNI to communicate between Java and C++ programs or easier with tools like javacpp?
It looks tomcat has implement a helper (jni.MMap) that is able to unmap/delete a mmap file
See the util-mmap in action, really easy:
public class MMapTesting {
public static void main(String[] args) throws IOException {
File file = new File("test");
MMapBuffer buffer = new MMapBuffer(file, 0, 1000, FileChannel.MapMode.READ_WRITE, ByteOrder.BIG_ENDIAN)) {
buffer.memory().intArray(0, 100).set(2, 234);
// calls unmap under the hood
buffer.close();
// here we call unmap automatically at the end of this try-resource block
try (MMapBuffer buffer = new MMapBuffer(file, FileChannel.MapMode.READ_WRITE, ByteOrder.BIG_ENDIAN)) {
System.out.println("length: " + buffer.memory().length());
IntArray arr = buffer.memory().intArray(0, buffer.memory().length() / 8);
// prints 234
System.out.println(arr.get(2));
}
}
}
From the Android Developers website:
A direct byte buffer whose content is a memory-mapped region of a file.
Mapped byte buffers are created via the FileChannel.map method. This class extends the ByteBuffer class with operations that are specific to memory-mapped file regions.
A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself is garbage-collected.
The content of a mapped byte buffer can change at any time, for example if the content of the corresponding region of the mapped file is changed by this program or another. Whether or not such changes occur, and when they occur, is operating-system dependent and therefore unspecified.
As for what I've understood from this text, is that there is no way to unmap the MappedByteBuffer using the Android Java SDK. Only using the NDK, like you said.
I've developed a Web Service in Java, and my client will upload an .ini file to the service using the API which definition is something like
public Response PostStreamInfo(#Context HttpServletRequest request,
#PathParam("UniqueId") String uniqueId, InputStream inputStream)
{
/* my code here */
}
Now I want to convert the inputStream back into .ini format, and retreive some value. However, all methods I found to get .ini content is read from real file, none of them talk about how to transfer from inputstream.
Is my goal possible? Because I don't want to Store the .ini file, Read it, and Delete it only for 1 value I need, I think that's a waste of time. Or should I just use Json format instead of InputStream to transfer the content? Thanks!
Are you dealing with actual INI files or Java property files?
It looks like there's an Apache class that you could use via an InputStreamReader and the read method for INI files.
If you're just dealing with Java property files, there are read methods that take InputStreams and also Readers.
I'm working on a chat application, and I need to process a get request for a file that has been uploaded to the database. I'm not sure if I should return an output stream or a file or what.
The idea is that it will be something like any other chat application where the image appears as message are loaded. Using an output stream seemed like the best option, but I wasn't sure how to create the output stream from the information in the database, which includes an id, checksum, name, size, and mime type.
So my questions are:
How should I approach this?
if output stream is the best way, what's the ideal way to implement it?
Any guidance is appreciated, please let me know if I can make the question more clear, or if more details are necessary to answer the question.
What I couldn't understand how to do is this: serve the image to the front-end/client code. As it turns out, it was super easy.
#GET #javax.ws.rs.Path("/file/{fileId}")
public Response getFile(#Context SecurityContext sc, #PathParam("id") long topicId, #PathParam("fileId") long fileId) {
TopicFile tFile = topicAccessor.getFile(fileId);
String fileLocation = "/server/uploads/" + tFile.getChecksum();
File file = new File(fileLocation);
return Response.ok(file, tFile.getType()).build();
}
Here TopicFile holds metadata for the file in the database, and the files are named their checksum.
So basically the solution to my problem was to return a Response. I hadn't thought of this earlier because I "inherited" this code, and I trusted that the previous person had god reason not to use the Response class.
I'm migrating my GAE app from the deprecated File API to Google Cloud Storage Client Library.
I used to persist the blobKey, but since there is partial support for it (as specified here) from now on I'll have to persist the object name.
Unfortunately the object name that comes from the GCS looks more or less like this
/gs/bucketname/819892hjd81dh19gf872g8211
as you can see, it also contains the bucket name
Here's the issue, every time I need to get the file for further processing (or to serve it in a servlet) I need to create an instance of GcsFileName(bucketName, objectName) which gives me something like
/bucketName/gs/bucketName/akahsdjahslagfasgfjkasd
which (of course) doesn't work.
so. my question is:
- how can I generate a GcsFileName form the objectName?
UPDATE
I tried using the objectName as BlobKey. But it just doesn't work :(
InputStream is = new BlobstoreInputStream(blobstoreService.createGsBlobKey("/gs/bucketName/akahsdjahslagfasgfjkasd"));
I got the usual answer
BlobstoreInputStream received an invalid blob key
How do I get the file using the ObjectName???
If you have persisted and retrieved e.g the string String objname worth e.g "/gs/bucketname/819892hjd81dh19gf872g8211", you could split it on "/" (String[] pieces = objname.split("/")) and use the pieces appropriately in the call to GcsFileName.
I want to send across resource(say a image) from some URL to front-end.
The typical way of doing this is to create a File and build the response. Is there any way in which I don't have to create the File in java code and still send the resource to front-end.
Front-end cannot access the URL due to some constraints.
Currently the Pseudo code looks like this.
File file = new File(fullPath);
FileUtils.copyURLToFile(url, file);
ResponseBuilder response = Response.ok(modulePDF);
I want to send content of URL to front-end without creating file. Is there any way?
What way did have in mind to actually obtain the file without creating a File object?
Not sure I fully understand the complete requirement, but you don't have to create a File object. You can send out a byte[], or simply write it to the response output stream by returning StreamingOutput.
#GET
public StreamingOutput getString() {
return new StreamingOutput(){
#Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
// write to the `out` stream
}
};
}
For anything more than that, you will have to greatly elaborate on your requirement. The information you've provided, (especially the highlighted line) doesn't quite paint a clear enough problem as to what actual problem is you are facing.