RE-Stream flash video from server to clients - java

I have an ipcamera that whenever multiple of users are connecting to it it becomes too slow.
I was thinking about getting the stream from the camera with my server and multiple of clients should be able to stream from the server instead of the poor ipcamera.
i set up a quick and dirty servlet just too see if it works :
#RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/x-shockwave-flash")
public String getVideoStream(Locale locale, Model model, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.info("Start");
// An IPcamera stream example
URL url = new URL("http://www.earthcam.com/swf/ads5.swf");
URLConnection yc = url.openConnection();
OutputStream out = response.getOutputStream();
InputStream in = yc.getInputStream();
String mimeType = "application/x-shockwave-flash";
byte[] bytes = new byte[100000];
int bytesRead;
response.setContentType(mimeType);
while ((bytesRead = in.read(bytes)) != -1) {
out.write(bytes, 0, bytesRead);
}
logger.info("End");
I believe this might work, my problem right now is that :
bytesRead = in.read(bytes)
reads only 61894 bytes and that's it :( why is that happening? am i trying to get the stream wrong?
btw: i tried to do this with xuggler, but i had an error that compressed-SWF not supported.
thanks

Your code is working perfectly. I just fetched ads5.swf from your server and it is, indeed, 61894 bytes in length. The problem you're facing is that the SWF file is just the movie player. After being downloaded, the player then fetches the video stream from the server. By default (if this is some kind of turn-key streaming solution), it's probably trying to get the stream from the same server where the SWF comes from.

Related

Zip file getting downloaded without .zip extension in Chrome through Java

I am trying to download a zip file from a fixed location present in server.
In my Rest method , I am just passing the file name from client (browser) .
(Please see below code ).
In my Rest method I am sending the zip file to the client.
The file gets downloaded on the browser without any issue.
My Issue is that the zip file gets downloaded on browser without .zip extension.
#RequestMapping(value = "/zip/{filePath}", method = RequestMethod.GET)
public #ResponseBody void downloadZip(#PathVariable("filePath") String filePath, HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletContext context = request.getServletContext();
File downloadFile = new File(filePath);
FileInputStream inputStream = new FileInputStream(downloadFile);
// get output stream of the response
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[(int) downloadFile.length()];
int bytesRead = -1;
// write bytes read from the input stream into the output stream
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
// get MIME type of the file
String mimeType = context.getMimeType(fullPath);
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
System.out.println("MIME type: " + mimeType);
// set content attributes for the response
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.length());
response.setHeader("Content-Disposition",
String.format("attachment; filename=\"%s\"", downloadFile.getName()));
logger.error("Filename = " + downloadFile.getName());
inputStream.close();
outStream.close();
}
PS: The file gets downloaded on some machine with ZIP and in some machine without ZIP. I have tested only on chrome (as per client requirement).
I think, there is an issue with the Chrome settings which I need to look upon (just a guess).
Can someone help upon this?
Thanks in advance....
Change the order between setting the response headers and shoving the file down the output stream - after all, the headers need to leave first.
[Edited]
"Why setting HttpServletResponse in starting effects the code."
Well, simple: the client is supposed to receive instructions of what to do with the payload by interpreting the HTTP response headers. If those are not set in the beginning, sending those headers at the end of the transmission comes too late. And this assumes the HttpServletResponse will actually send those headers when invoked with setHeader, which is a big assumption - I suspect those headers will not actually be sent after calling response.getOutputStream - it is unlikely the response will buffer the entire payload to wait for the caller to specify those headers.

Return file from Spring #Controller having OutputStream

I want to return a file from a Spring controller. I already have API that can give me any implementation of OutputStream and then I need to send it to a user.
So the flow is something like that:
getting outputstream -> service passes this outputstream to controller -> controller has to send it to a user
I think I need inputstream to do it and I have also found Apache Commons api feature that looks like this:
IOUtils.copy(InputStream is, OutputStream os)
but the problem is, it converts it to the other side -> not from os to is, but from is to os.
Edit
to be clear, because I see the answers are not hitting right thing:
I use Dropbox api and recieve file in OutputStream and I want this output stream to be sent to user while entering some URL
FileOutputStream outputStream = new FileOutputStream(); //can be any instance of OutputStream
DbxEntry.File downloadedFile = client.getFile("/fileName.mp3", null, outputStream);
Thats why i was talking about converting outputstream to inputstream, but have no idea how to do it. Furthermore, I suppose that there is better way to solve this (maybe return byte array somehow from outputstream)
I was trying to pass servlet outputstream [response.getOutputstream()] through parameter to the method that downloads file from dropbox, but it didnt work, at all
Edit 2
The "flow" of my app is something like this: #Joeblade
User enters url: /download/{file_name}
Spring Controller captures the url and calls the #Service layer to download the file and pass it to that controller:
#RequestMapping(value = "download/{name}", method = RequestMethod.GET)
public void getFileByName(#PathVariable("name") final String name, HttpServletResponse response) throws IOException {
response.setContentType("audio/mpeg3");
response.setHeader("Content-Disposition", "attachment; filename=" + name);
service.callSomeMethodAndRecieveDownloadedFileInSomeForm(name); // <- and this file(InputStream/OutputStream/byte[] array/File object/MultipartFile I dont really know..) has to be sent to the user
}
Now the #Service calls Dropbox API and downloads the file by specified file_name, and puts it all to the OutputStream, and then passes it (in some form.. maybe OutputStream, byte[] array or any other object - I dont know which is better to use) to the controller:
public SomeObjectThatContainsFileForExamplePipedInputStream callSomeMethodAndRecieveDownloadedFileInSomeForm(final String name) throws IOException {
//here any instance of OutputStream - it needs to be passed to client.getFile lower (for now it is PipedOutputStream)
PipedInputStream inputStream = new PipedInputStream(); // for now
PipedOutputStream outputStream = new PipedOutputStream(inputStream);
//some dropbox client object
DbxClient client = new DbxClient();
try {
//important part - Dropbox API downloads the file from Dropbox servers to the outputstream object passed as the third parameter
client.getFile("/" + name, null, outputStream);
} catch (DbxException e){
e.printStackTrace();
} finally {
outputStream.close();
}
return inputStream;
}
Controler recieves the file (I dont know, at all, in which form as I said upper) and passes it then to the user
So the thing is to recieve OutputStream with the downloaded file by calling dropboxClient.getFile() method and then this OutputStream that contains the downloaded file, has to be sent to the user, how to do this?
Get the OutputStream from the HttpServletResponse and write the file to it (in this example using IOUtils from Apache Commons)
#RequestMapping(value = "/download", method = RequestMethod.GET)
public void download(HttpServletResponse response) {
...
InputStream inputStream = new FileInputStream(new File(PATH_TO_FILE)); //load the file
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
...
}
Make sure you use a try/catch to close the streams in case of an exception.
The most preferable solution is to use InputStreamResource with ResponseEntity. All you need is set Content-Length manually:
#RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity download() throws IOException {
String filePath = "PATH_HERE";
InputStream inputStream = new FileInputStream(new File(filePath));
InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
HttpHeaders headers = new HttpHeaders();
headers.setContentLength(Files.size(Paths.get(filePath)));
return new ResponseEntity(inputStreamResource, headers, HttpStatus.OK);
}
You could use the ByteArrayOutputStream and ByteArrayInputStream. Example:
// A ByteArrayOutputStream holds the content in memory
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Do stuff with your OutputStream
// To convert it to a byte[] - simply use
final byte[] bytes = outputStream.toByteArray();
// To convert bytes to an InputStream, use a ByteArrayInputStream
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
You can do the same with other stream pairs. E.g. the file streams:
// Create a FileOutputStream
FileOutputStream fos = new FileOutputStream("filename.txt");
// Write contents to file
// Always close the stream, preferably in a try-with-resources block
fos.close();
// The, convert the file contents to an input stream
final InputStream fileInputStream = new FileInputStream("filename.txt");
And, when using Spring MVC you can definitely return a byte[] that contains your file. Just make sure that you annotate your response with #ResponseBody. Something like this:
#ResponseBody
#RequestMapping("/myurl/{filename:.*}")
public byte[] serveFile(#PathVariable("file"} String file) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DbxEntry.File downloadedFile = client.getFile("/" + filename, null, outputStream);
return outputStream.toByteArray();
}
I recommend reading this answer
#ResponseBody
#RequestMapping("/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
answered by michal.kreuzman
I was going to write something similar myself but ofcourse it's already been answered.
If you want to just pass the stream instead of first getting everything in memory you could use this answer
I haven't tested this (not at work) but it looks legit :)
#RequestMapping(value = "report1", method = RequestMethod.GET, produces = "application/pdf")
#ResponseBody
public void getReport1(OutputStream out) {
InputStream in; // retrieve this from wherever you are receiving your stream
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
in.close();
out.flush(); // out.close?
}
The thing is, this is pretty much the same as IOUtils.copy / IOUtils.copyLarge does. line: 2128
Which you say copies the wrong direction.
However first make sure you understand what you ask. If you want to read from an outputstream(object for writing) and write to an input stream (object to read from) then I think what you really want is to write to an object that also supplies a read option.
for that you could use a PipedInputStream and PipedOutputStream. These are connected together so that bytes written to the outputstream are available to be read from the corresponding input stream.
so in the location where you are receiving the bytes I assume you are writing bytes to an outputstream.
there do this:
// set up the input/output stream so that bytes written to writeToHere are available to be read from readFromhere
PipedInputStream readFromHere = new PipedInputStream();
PipedOutputStream writeToHere = new PipedOutputStream(readFromHere);
// write to the outputstream as you like
writeToHere.write(...)
// or pass it as an outputstream to an external method
someMather(writeToHere);
// when you're done close this end.
writeToHere.close();
// then whenever you like, read from the inputstream
IOUtils.copy(readFromHere, out, new byte[1024]);
If you use IOUtils.copy it will continue to read until the outputstream is closed. so make sure that it is already closed before starting (if you run write/read on the same thread) or use another thread to write to the output buffer and close it at the end.
If this is still not what you're looking for then you'll have to refine your question.
The most memory-efficient solution in your case would be to pass the response OutputStream right to the Dropbox API:
#GetMapping(value = "download/{name}")
public void getFileByName(#PathVariable("name") final String name, HttpServletResponse response)
throws IOException, DbxException {
response.setContentType("audio/mpeg3");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + name + "\"");
response.setContentLength(filesize); // if you know size of the file in advance
new DbxClient().getFile("/" + name, null, response.getOutputStream());
}
Data read by the API will be sent directly to the user. No additional byte buffer of any type is required.
As for PipedInputStream/PipedOutputStream, they are intended for the blocking communication between 2 threads. PipedOutputStream blocks writing thread after 1024 bytes (by default) until some other thread start reading from the end of the pipe (PipedInputStream).
One thing to keep in mind when writing to the response outputstream is that it is a very good idea to call flush() on whatever writer that you've wrapped it with periodically. The reason for this is that a broken connection (for example caused by a user canceling a download) may not end up throwing an exception for a long time, if ever. This can effectively be a resource leak on your container.

get the PDF from a URL and push it to the clients browser for download

I have PDFs mounted on an external server. I have to access them in my Java servlet and push them to the clients browser. The PDF should get downloaded directly or it may open a 'SAVE or OPEN' dialog window.
This is what i am trying in my code but it could not do much.
URL url = new URL("http://www01/manuals/zseries.pdf");
ByteArrayOutputStream bais = new ByteArrayOutputStream();
InputStream in = url.openStream();
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int n =0;
while ( (n = in.read(chunk)) != -1 ) {
bais.write(chunk, 0, n);
}
I have tried many ways to do this but could not succeed. I welcome if you have any good method to do this!
When you read the data, you get it inside your program memory, which is on the server side. To get it to the user's browser, you have to also write everything that you have read.
Before you start writing, though, you should give some appropriate headers.
Indicate that you are sending over a PDF file, by setting the mime type
Set the content length.
Indicate that the file is intended for download rather than showing inside the browser.
To set the mime type, use
response.setContentType("application/pdf");
To set the content length, assuming it's the same content length that you get from the URL, use:
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect();
if ( connection.getResponseCode() == 200 ) {
int contentLength = connection.getContentLength();
response.setContentLength( contentLength );
To indicate that you want the file to be downloaded, use:
response.setHeader( "Content-Disposition", "attachment; filename=\"zseries.pdf\"";
(Take care to change the file name to whatever you want the user to see in the save dialog box)
Finally, get the input stream from the URLConnection you just opened, get the servlet's response output stream, and start reading from one and writing to the other:
InputStream pdfSource = connection.getInputStream();
OutputStream pdfTarget = response.getOutputStream();
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int n =0;
while ( (n = pdfSource.read(chunk)) != -1 ) {
pdfTarget.write(chunk, 0, n);
}
} // End of if
Remember to use try/catch around this, because most of these methods throw IOException, timeout exceptions etc., and to finally close both streams. Also remember to do something meaningful (like give an error output) in case the response was not 200.
You could transfer the byte array to the client, then use Itext to "stamp" the pdf in a new file. After that use java.awt.Desktop to lauch the file.
public static void lauchPdf(byte[] bytes, String fileName) throws DocumentException, IOException{
PdfReader reader = new PdfReader(bytes);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(fileName));
stamper.close();
Desktop dt = Desktop.getDesktop();
dt.browse(getFileURI(fileName));
}
You don't need to push anything (hope you really don't, because actually you can't). From the perspective of the browser making the request, you could get the PDF from the database, generate it on the fly or read it from the filesystem (which is your case). So, let's say you have this in your HTML:
DOWNLOAD FILE
you need to register a servlet for /dl/* and implement the doGet(req, resp) like this:
public void doGet(
HttpServletRequest req
, HttpServletResponse resp
) throws IOException {
resp.setContentType("application/pdf");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + suggestFilename(req) + "\"");
// Then copy the stream, for example using IOUtils.copy ...
// lookup the URL from the bits after /dl/*
URL url = getURLFromRequest(req);
InputStream in = url.openConnection().getInputStream();
IOUtils.copy(in, resp.getOutputStream());
fin.close();
}
IOUtils is from Apache Commons IO (or just write your own while loop)

Custom proxy copying PDF

I am writing a custom proxy as a web service client for our main application, which uses REST web services. For security reasons, I am trying to use a servlet on the client side as a proxy to retrieve a PDF from the server side and then display that in the application web browser through the client app.
As the heart of this, I have this piece of code:
protected void copy(HttpResponse fromResponse, HttpServletResponse toResponse)
throws IOException{
HttpEntity entity = fromResponse.getEntity();
for(Header header:fromResponse.getAllHeaders()){
toResponse.setHeader(header.getName(), header.getValue());
}
BufferedInputStream inputStream = new BufferedInputStream(entity.getContent());
BufferedOutputStream outputStream = new BufferedOutputStream(toResponse.getOutputStream());
int oneByte;
int byteCount = 0;
while((oneByte = inputStream.read()) >= 0){
outputStream.write(oneByte);
++byteCount;
}
log.debug("Bytes copied:" + byteCount);
which should copy the PDF from the returned output stream to the current output stream and then return it.
When I run it, though, I get an error from Adobe Reader saying the file is damaged and could not be repaired. When I run the URL directly the file is fine, so it has to be something in the handoff. The byteCount is equal to the PDF file size.
Does anyone have an idea what the problem is?
By doing
while((inputStream.read(buffer)) >= 0){
outputStream.write(buffer);
}
you will always write the full length of buffer, regardless of its effective content length as write can only look at the buffer's size to determine what to write.
int count;
while(((count = inputStream.read(buffer))) >= 0){
outputStream.write(buffer,0,count);
}
should take care of that problem.
I closed outputStream after writing to it and it works fine.
I didn't think you were supposed to do that?

Returning CSV file from Servlet using ServletOutputStream over HTTPS in Internet Explorer

I have a Servlet which is returning a csv file that is 'working' over HTTP in both internet explorer and firefox. When I execute the same Servlet over HTTPS only firefox continues to download the csv file over HTTPS. I don't think this is necessarily an Internet 6 or 7 issue described on MSDN :
The message is:
Internet Explorer cannot download
data.csv from mydomain.com Internet
Explorer was not able to open this
Internet site. The requested site is
either unavailable or cannot be found.
Please try again later.
Please note that the site is still 'up' after this message and you can continue to browse the site, its just the download of the CSV that prompts this message. I have been able to access similar files over https on IE from other j2ee applications so I believe it is our code. Should we not be closing the bufferedOutputStream?
UPDATE
whether to close or not to close the output stream:
I asked this question on the java posse forums and the discussion there is also insightful. In the end it seems that no container should rely on the 'client' (your servlet code in this case) to close this output stream. So if your failure to close the stream in your servlet causes a problem it is more a reflection on the poor implementation of your servlet container than your code. I sited the behavior of the IDEs and tutortials from Sun, Oracle and BEA and how they are also inconsistent in whether they close the stream or not.
About IE specific behavior: In our case a separate product 'Oracle Web Cache' was introducing the additional header values which impacts Internet explorer only because of the way IE implements the 'No Cache' requirement (see the MSDN article).
The code is:
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
ServletOutputStream out = null;
ByteArrayInputStream byteArrayInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
response.setContentType("text/csv");
String disposition = "attachment; fileName=data.csv";
response.setHeader("Content-Disposition", disposition);
out = response.getOutputStream();
byte[] blobData = dao.getCSV();
//setup the input as the blob to write out to the client
byteArrayInputStream = new ByteArrayInputStream(blobData);
bufferedOutputStream = new BufferedOutputStream(out);
int length = blobData.length;
response.setContentLength(length);
//byte[] buff = new byte[length];
byte[] buff = new byte[(1024 * 1024) * 2];
//now lets shove the data down
int bytesRead;
// Simple read/write loop.
while (-1 !=
(bytesRead = byteArrayInputStream.read(buff, 0, buff.length))) {
bufferedOutputStream.write(buff, 0, bytesRead);
}
out.flush();
out.close();
} catch (Exception e) {
System.err.println(e); throw e;
} finally {
if (out != null)
out.close();
if (byteArrayInputStream != null) {
byteArrayInputStream.close();
}
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
}
}
I am really confused about your "from back through the breast into the head" write mechanism. Why not simple (the servlet output stream will be bufferend, thats container stuff):
byte[] csv = dao.getCSV();
response.setContentType("text/csv");
response.setHeader("Content-Disposition", "attachment; filename=data.csv"));
reponse.setContentLength(csv.length);
ServletOutputStream out = response.getOutputStream();
out.write(csv);
There should also be no need to flush the output stream nor to close.
The header content should not be parsed case sensitive by IE, but who knows: do not camelcase fileName. The next question is the encoding. CSV is text, so you should use getWriter() instead or getOutputStream() and set the content type to "text/csv; charset=UTF-8" for example. But the dao should provide the CSV as String instead of byte[].
The servlet code has nothing to d with HTTPS, so the protocol does not matter from the server side. You may test the servlet from localhost with HTTP i hope.
What about filters in your application? A filter may als set an HTTP header (or as footer) with cache-control for example.

Categories

Resources