Is it possible to get multiple files in one GET request? - java

I'm working on a Spring Boot project and I need users to be able to send a GET request to my service and get multiple files as response without pages involved. This is what I have right now:
public void get(HttpServletResponse response,FileInputStream[] streams){
String boundary = "------customBoundary";
response.setContentType("multipart/mixed; boundary="+boundary);
String contentType = "Content-type: audio/mpeg";
var outputStream = response.getOutputStream();
for(FileInputStream ois:streams){
if(ois!=null){
outputStream.println("--"+boundary);
outputStream.println(contentType);
outputStream.println("Content-disposition: attachment; filename=\"random.mp3\"");
outputStream.println();
try{
BufferedInputStream inputStream = new BufferedInputStream(ois);
int swap;
while ((swap = inputStream.read()) != -1) {
outputStream.write(swap);
}
outputStream.println();
outputStream.flush();
ois.close();
inputStream.close();
}catch (Exception s){
s.printStackTrace();
}
}
}
outputStream.println("--"+boundary+"--");
outputStream.flush();
outputStream.close();
}
If you access the service on Chrome, it'll automatically download a file name "download", as the browser sees the whole this as one giant file attachment.
I think the problem may be at the ContentType part, so far I tried "multipart/x-mixed-replace" as some post here suggested, only to find out later that it was removed several years ago. I tried "multipart/form-data" out of desperation and that didn't work.
The files I'm dealing with are media files, it's quite large and I'm running this on a crappy server, so putting them in one zip file is not an option.
Most of the result I found more or less have something to do with html or javascript, but what I'm doing here is intended as an API for other services to call so I'm hoping it can be done with pure java.

Related

Soap service can't write to XML file

Simple Soap service running on Axis engine on Tomcat v9.0 server needs to read and write to XML files. I developed soap service in Eclipse like a dynamic web project, so the XML files are in the WebContent->WEB-INF->resources->...
When i read the files everything works fine, but when i want to write to the files i get InvocationTargetException. Since i read files normaly, I guess that i'm not opening stream as i should when i write in the files, so can anyone guide me how to do this properly?
Here's the method for reading the file, and this WORKS:
public Station deserialization(String name, String path) {
Station s=null;
try {
URL url=getClass().getClassLoader().getResource(path+File.separator+name+".xml");
InputStream is=url.openStream();
XMLDecoder decoder = new XMLDecoder(is);
s=(Station) decoder.readObject();
decoder.close();
} catch (Exception e) {
Main.LOGGER.info("Station deserializaton was not successful!");
}
return s;
}
and here's the method for writing into the file, this DOESN'T work:
public boolean serialize(Station s, String path) {
try {
URL url=getClass().getClassLoader() .getResource(path+File.separator+s.getName()+".xml");
URLConnection con=url.openConnection();
con.setDoOutput(true);
OutputStream out=con.getOutputStream();
XMLEncoder encoder = new XMLEncoder(out);
encoder.writeObject(s);
encoder.close();
} catch (Exception e) {
Main.LOGGER.info("Station serialization was not successful!");
return false;
}
return true;
}
My real question here is how come the same principle work when reading the file and doesn't work with writing into the file? File paths are the same in both methods.
I found what was the problem. Turns out you can read files with URL, but you cant write to URL or URLConnection. I had to use FileOutputStream for writing into the files:
XMLEncoder encoder=new XMLEncoder(new FileOutputStream("file_path"));
You can also keep the files in WebContent->WEB-INF (if you develop web service in Eclipse) if you want web service to use them, becuse it then makes copies of them.
Just keep an eye on the path that you are providing, double check if it is the right one!

Creating mp4 file doesn't remove tmp files

I'm trying to write an InputStream that is an mp4 that I get from calling an external SOAP service, when I do so, it always generates this tmp files for my chosen temporary directory(java.io.tmpdir) that aren't removable and stay after the writing is done.
Writing images that I also get from the SOAP service works normal without the permanent tmp on the directory. I'm using java 1.8 SpringBoot
tmp files
This is what I'm doing:
File targetFile = new File("D:/archive/video.mp4");
targetFile.getParentFile().mkdirs();
targetFile.setWritable(true);
InputStream inputStream = filesToWrite.getInputStream();
OutputStream outputStream = new FileOutputStream(targetFile);
try {
int byteRead;
while ((byteRead = inputStream.read()) != -1) {
outputStream.write(byteRead);
}
} catch (IOException e) {
logger.fatal("Error# SaveFilesThread for guid: " + guid, e);
}finally {
try {
inputStream.close();
outputStream.flush();
outputStream.close();
}catch (Exception e){
e.printStackTrace();
}
also tried:
byte data[] = IOUtils.toByteArray(inputStream);
Path file = Paths.get("video.mp4");
Files.write(file, data);
And from apache commons IO:
FileUtils.copyInputStreamToFile(initialStream, targetFile);
When your code starts, the damage is already done. Your code is not the source of the temporary files (It's.. a ton of work for something that could be done so much simpler, though, see below), it's the framework that ends up handing you that filesToWrite variable.
It is somewhat likely that you can hook in at an earlier point and get the raw inputstream representing the socket or HTTP connection, and start saving the files straight from there. Alternatively, Perhaps filesToWrite has a way to get at the files themselves, in which case you can just move them into place instead of copying them over.
But, your code to do this is a mess, it has bad exception handling, and leaks memory, and is way too much code for a simple job, and is possibly 2000x to 10000x slower than needed depending on your harddisk (I'm not exaggerating, calling single-byte read() on unbuffered streams is thousands of times slower!)
// add `throws IOException` to your method signature.
// it saves files, it's supposed to throw IOException,
// 'doing I/O' is in the very definition of your method!
try (InputStream in = filesToWrite.getInputStream();
OutputStream out = new FileOutputStream(targetFile)) {
in.transferTo(out);
}
That's it. That solves all the problems - no leaks, no speed loss, tiny amount of code, fixes the deplorable error handling (which, here, is 'log something to the log, then print something to standard out, then potentially leak a bunch of resources, then don't tell the calling code anything went wrong and return exactly as if the copy operation succeeded).

Open file content in new tab in browser

I want to show the file content in new tab in browser. What i have done is this:
int BUFF_SIZE = 102400;
FileInputStream is = null;
byte[] buffer = new byte[BUFF_SIZE];
int a = -1;
try
{
is = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
while((a = is.read(buffer)) != -1)
{
out.write(buffer);
}
out.flush();
out.close();
ServletOutputStream os = null;
os = response.getOutputStream();
os.write(out.toByteArray());
os.close();
is.close();
}
catch(Exception e)
{
// Exception handling
}
But this is leading to download of the file instead of opening the file-content in new tab.
I am not able to find what i am doing wrong.
Any help would be great!!
Actually, all you should need to do now is add JQuery to your webpage, and use JQUery.get. Once you get the html from the servlet, use jquery or javascript to set the text in your tab.
BTW, you might want to set other details on the servlet output stream, like file type, length etc. Just a thought
You could also try this with the omnifaces library
Faces.sendFile(file, false);//true makes it as an attachment
more information on http://omnifaces.org/docs/javadoc/1.8/org/omnifaces/util/Faces.html#sendFile(java.io.File,%20boolean)
A web application might not even know what is a brower. It receives requests through HTTP protocol and send responses through same protocol. The protocol by itsels knows nothing about browsers and tabs.
You must use javascript for anything that happens at browser level. Other answers adviced you to use jQuery. It is a well known javascript library that hides differences between browsers, but there are others around (dojo, extJs, ...) : Google and make your choice.
By the way, if all you want is open an URL in a new tab, that's one of the very few operations that you can do at HTML level. Just look at this example
from W3Schools.com :
Visit W3Schools!
that opens www.w3schools.com in a new tab (if browser has tabs what is now common) or a new window.

Zip file turnout out to be empty

I'm working on a HTTP server in Java, which for testing purposes is running under Windows 8.1.
The way it's coded makes it so when a certain parameter is set, it changes the header of the HTTP file and sends the file through the socket with something that works kind of like:
socket.outputStream.write(filter.read());
Assume that the communication works fine, since I have tested it with various other filters and it works perfectly.
One of the filters is supposed to grab the HTML file, zip it and then send it to the client, without creating the file in the server machine. This is the header:
"HTTP/1.1 200 OK\nContent-Type: application/zip\nContent-Disposition: filename=\"" + request + ".zip\"\n";
Afterwards, I set my filter to a class I created (which is copied below) and send the file. My problem is that even though the server is definitively sending data, the client only downloads an empty zip file, with nothing inside.
I've been stuck with this issue for a few days, I can't seem to figure out what's wrong. I think that there's something wrong with how I create the entry or maybe how I close the outputs. I can't be sure.
I'd really appreciate any advice that could be given to me on this issue. Thanks for your attention.
class ZipFilterInputStream extends FilterInputStream
{
protected ZipFilterInputStream(InputStream inputToFilter) throws IOException
{
super(inputToFilter);
//Get the stuff ready for compression
ByteArrayOutputStream out = new ByteArrayOutputStream();
ZipOutputStream zout = new ZipOutputStream(out);
zout.putNextEntry(new ZipEntry("file.html"));
//Compress the stream
int data = in.read();
while (data != -1)
{
zout.write(data);
data = in.read();
}
zout.closeEntry();
zout.finish();
//Get the stream ready for reading.
in = new ByteArrayInputStream(out.toByteArray());
out.close();
}
public int read() throws IOException
{
return in.read();
}
}

Problem sending XML via HTTP

I want to have an application which parses various RSS feeds and send the information to a remote server. The information is sent in xml format via http. At first I tried to deploy this application on my own server, so I send the xml using the method shown in this tutorial by Java Tips. Here is my code which is replicated from the example:
First Method
String strURL = "http://localhost/readme/readme_xml";
String strXMLFilename = "output.xml";
File input = new File(strXMLFilename);
PostMethod post = new PostMethod(strURL);
post.setRequestEntity(new InputStreamRequestEntity(
new FileInputStream(input), input.length()));
post.setRequestHeader(
"Content-type", "text/xml; charset=ISO-8859-1");
HttpClient httpclient = new HttpClient();
try {
int result = httpclient.executeMethod(post);
System.out.println("Response status code: " + result);
System.out.println("Response body: ");
System.out.println(post.getResponseBodyAsString());
} finally {
post.releaseConnection();
}
This works perfectly (I even tested using a remote server outside the localhost). Then, somehow I cant use my own server to deploy this application, so I decided to migrate to Google Apps Engine. One thing about it, as we know it, is that not all libraries are allowed in the environment. So I try another method shown in ExampleDepot.com (I can't find where the exact url though) as below:
Second Method
try {
/* fill up this url with the remote server url */
URL url = new URL("http://localhost/readme/readme_xml");
FileReader fr = new FileReader("output.xml");
char[] buffer = new char[1024*10];
int len = 0;
if ((len = fr.read(buffer)) != -1){
/* send http request to remote server */
URLConnection conn = url.openConnection();
conn.setRequestProperty("Content-Type","text/xml;charset=ISO-8859-1"); /* need to specify the content type */
conn.setDoOutput(true);
conn.setDoOutput(true);
PrintWriter pw = new PrintWriter(conn.getOutputStream());
pw.write(buffer, 0, len);
pw.flush();
/* receive response from remote server*/
BufferedReader bf = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String input = null;
while ((input = bf.readLine()) != null){
System.out.println(input);
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
The second method though, doesn't work and gives the following error (I use SimpleXMLElement (php) object to parse xml in the remote hosting):
Error message from remote server
Here's the php code from the remote server (In here, I just want the SimpleXMLElement to parse the xml without doing anything else fancy for now)
$xml = new SimpleXMLElement('php://input', NULL, TRUE);
foreach ($xml -> attributes() as $name => $val){
echo "[".$name."] = ".$val."\n";
}
I thought the cause of this problem is the malfunction xml file (because the eclipse IDE indicates there's error of "invalid byte 1 of 1-byte utf-8 sequence"). Then I use the same exact input xml file to the first method, but it still works perfectly.
So is there any adjustment that I need to make to the second method? Or is there any other method that I can use to send xml file to remote server? Let me know if I need to add some other details. Thanks for your help.
NOTE: I actually solved this problem by using the solution given in the comments. I didn't use approaches suggested in the answers, even though those answers are pretty useful. So, I didn't select the best answer out of those answers given. Nonetheless, I still appreciate all of your helps, thus deserve my upvote. Cheers!
I guess you need to change the content type to multipart/form-data. See an already answered question in detailed. The file upload is discussed at the bottom of this example
I would, as the first answer suggest, read the file with an InputStream. Converting from byte to char and back again is unnecessary and a source of error. Also, verify that the input file really is using the ISO-8859-1 encoding.
UPDATE:
When using a FileReader, you accept the default encoding (i.e. how to make chars from bytes). This encoding must match the encoding used for the input file, otherwise there's a great risk that the result is corrupted. The default Java encoding is different for different platforms, so it is generally not a good idea to rely on it.
In your second example, there's no reason to read the file as characters, since it will be sent on the wire as bytes anyway. Using byte streams all the way also avoids the encoding issue (apart from the information in the content-type header).
never read a file as chars unless you are reading a text file. xml is not text, it is a binary format. copy the file using normal InputStreams and byte[]s.
also, as #beny23 suggested in his comment, make sure you always copy streams using a loop, not a single read() (even if your buffer is big enough, it is not guaranteed that the InputStream will give you all the bytes in one call, even for a FileInputStream).

Categories