I send on server multipart/form-data but not Error. No nothing! Simple 200 code request. I try to send multipart self.
I add header:
connection.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
And send body:
--------b3a2fb0a4742753e0c507301a6bcb2bc
Content-Disposition: form-data; name="Test"
test
--------b3a2fb0a4742753e0c507301a6bcb2bc--
But PHP not showing $_POST variable Test
Java-code
void onWriteBody(String boundary, OutputStream outputStream) throws Exception {
for (int i = 0; i < data.size(); i++) {
// Reference for data
Data data = this.data.get(i);
// Start multi-part/data
outputStream.write((boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
// Add content-disposition
outputStream.write(("Content-Disposition: form-data; name=\"" + URLEncoder.encode(data.getName(), "UTF-8") + "\"").getBytes(StandardCharsets.UTF_8));
// If file then add tag for file
if (data instanceof Data.File) {
outputStream.write(("; filename=\"" + URLEncoder.encode(((Data.File) data).getFileName(), "UTF-8") + "\"\r\n").getBytes(StandardCharsets.UTF_8));
outputStream.write(("Content-Type: " + ((Data.File) data).getContentType()).getBytes(StandardCharsets.UTF_8));
}
// Add new line
outputStream.write("\r\n\r\n".getBytes());
// Write to output stream
this.writeToOutputStream(outputStream, data);
// End multi-part/data
outputStream.write(("\r\n" + boundary + (i == this.data.size() - 1 ? "--" : "\r\n")).getBytes(StandardCharsets.UTF_8));
}
// After end the operation close output stream
outputStream.flush();
outputStream.close();
}
Related
I am sending files via post method using HttpURLConnection. I am sending with the file a parameter which is 'student_id'. The code is working fine when sending one file in each post request. But, how can I update the code below to send multiple files in one post request where all the files belong to the same 'student_id'?
try{
File textFile2 = new File("info.txt");
URL url = new URL("htttp://wwww.students.com");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setChunkedStreamingMode(0);
urlConnection.setDoOutput(true);
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
OutputStream output = urlConnection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"student_id\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
writer.append(CRLF).append("25").append(CRLF).flush();
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"newfile\"; filename=\"" + textFile2.getName() + "\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
writer.append(CRLF).flush();
Files.copy(textFile2.toPath(), output);//copies all bytes in a file to the output stream
output.flush(); // Important before continuing with writer!
writer.append(CRLF).flush();
writer.append("--" + boundary + "--").append(CRLF).flush();
InputStream responseStream;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
I tried to add 'multiple' with the parameter in 'newfile' but it is not working
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"newfile\" multiple; filename=\"" + textFile2.getName() + "\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
writer.append(CRLF).flush();
Files.copy(textFile2.toPath(), output);//copies all bytes in a file to the output stream
output.flush(); // Important before continuing with writer!
writer.append(CRLF).flush();
writer.append("--" + boundary + "--").append(CRLF).flush();
Seems that you are trying to post a multipart/form-data request with 1 form field with name parameter of student_id and multiple file part to upload files.
You can send multiple filesby supplying each file in a separate part but all with the same name parameter.
For example, you can upload a file of textFile1 by sending the first file part with name parameter of newfile:
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"newfile\"; filename=\"" + textFile1.getName()+ "\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
writer.append("Content-Transfer-Encoding: binary").append(CRLF);
writer.append(CRLF);
writer.flush();
FileInputStream inputStream = new FileInputStream(textFile1);
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
writer.append(CRLF);
writer.flush();
Then, you can upload another file of textFile2 by sending file part with same name parameter of newfile:
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"newfile\"; filename=\"" + textFile2.getName()+ "\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
writer.append("Content-Transfer-Encoding: binary").append(CRLF);
writer.append(CRLF);
writer.flush();
FileInputStream inputStream = new FileInputStream(textFile2);
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
writer.append(CRLF);
writer.flush();
As you can see, the code are almost the same except the file to upload. It is recommend to put the code into a method and call it to send each file part.
I've got a Node.js API that uses multer to handle multipart POST requests.
I am able to create a simple HTML form and POST images from there successfully.
I can also use postman to get the request to go through, but the req.file is always undefined on the server when I try to use my java method (below).
Called Method
public static void testPostImage() throws Exception {
String charset = "UTF-8";
String requestURL = "http://localhost:3000/accounts/2/details";
try {
File outputfile = new File(Util.getWorkingDirectory() + "/details");
MultipartUtility multipart = new MultipartUtility(requestURL, charset);
multipart.addFilePart("details", new File(outputfile.getAbsolutePath()));
List<String> response = multipart.finish();
General.println("SERVER REPLIED:");
for (String line : response) {
General.println(line);
}
} catch (IOException ex) {
General.println(ex);
}
}
The MultipartUtility class comes from this tutorial.
The contents of the constructor and 'addFilePart'...
Constructor
public MultipartUtility(String requestURL, String charset)
throws IOException {
this.charset = charset;
// creates a unique boundary based on time stamp
boundary = "===" + System.currentTimeMillis() + "===";
URL url = new URL(requestURL);
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setUseCaches(false);
httpConn.setDoOutput(true); // indicates POST method
httpConn.setDoInput(true);
httpConn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundary);
httpConn.setRequestProperty("User-Agent", "CodeJava Agent");
httpConn.setRequestProperty("Test", "Bonjour");
outputStream = httpConn.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
true);
}
addFilePart
private static final String LINE_FEED = "\r\n";
public void addFilePart(String fieldName, File uploadFile)
throws IOException {
General.println("Sending to " + fieldName + ": " + uploadFile.getAbsolutePath());
// Add boundary
writer.append("--" + boundary).append(LINE_FEED);
// Add form data
writer.append("Content-Disposition: form-data;"
+ "name=\"myFile\";"
+ "filename=\"" + fieldName + "\""
+ "\nContent-Type: text/plain\n\n").append(LINE_FEED);
writer.append("Content-Type: " + "multipart/form-data").append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
FileInputStream inputStream = new FileInputStream(uploadFile);
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
writer.append(LINE_FEED);
writer.flush();
}
Their might be more to it but you don't have the right line ending between your Content-disposition and Content-type header:
// Add form data
writer.append("Content-Disposition: form-data;"
+ "name=\"myFile\";"
+ "filename=\"" + fieldName + "\"").append(LINE_FEED);
writer.append("Content-Type: text/plain").append(LINE_FEED).append(LINE_FEED); // need 2 line feeds
Also you are mixing the order/type of headers
writer.append("Content-Type: " + "multipart/form-data").append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
Should come before your encoded data.
See examples here
A little history before my question, I posted a question about uploading multiple files to coldfusion using HTML5's multipart/form-data. And its worked beautifully.
Can you isolate code from being seen from CF10 compiler?
Our client has finally requested some unit tests for the RESTful functions that I have put together, and I've been able to get quite a lot done, but I've hit a roadblock with the massUpload function that I designed above.
Sorry for the long question, putting down what is relevant to the problem.
Here is the code in question:
Unit Test Code:
//Outside class calling sendHTTPrequest
HashMap<String,String> map = new HashMap<String,String>();
HashMap<String,File> getFiles = getFirstFileList();
map.put("testMethod", "massUploadTest");
map.put("method", "massUpload");
map.put("valueString1", valueString1);
map.put("valueString2", valueString2);
map.put("valueNumeric3", valueNumeric3);
map.put("valueBoolean4", valueBoolean4);
map.put("valueString5", valueString5);
map.put("valueBoolean6", valueBoolean6);
map.put("valueString7", valueString7);
try {
sendHTTPrequest(map, getFiles);
} catch(RuntimeException e) {
throw new RuntimeException("Fatal error in massUpload\n"
+ e.getMessage());
}
//End Call class code
Coldfusion function:
<cffunction name="massUpload" access="remote" returntype="string">
<cfargument name="valueString1" type="string" required="false">
<cfargument name="valueString2" type="string" required="false">
<cfargument name="valueNumeric3" type="numeric" required="false" default=0>
<cfargument name="valueBoolean4" type="boolean" required="true" default="false">
<cfargument name="valueString5" type="string" required="false">
<cfargument name="valueBoolean6" type="boolean" required="false" default="true">
<cfargument name="valueString7" type="string" required="true">
<!--- massUpload code --->
</cffunction>
So here is the code that is causing the major problems. I've tried different approaches to get the POST to work so obviously the code has problems. I'm trying to do this without downloading multiple software/libraries, but if there is no other way, then I'll make the necessary arrangements. Otherwise, would love to get this done with the standard Java library.
Function code:
//sendHTTPrequest method code
protected static final String BASE_URI = "<webaddress>/rest.cfc";
protected static final String CHARSET = "UTF-8";
protected String response;
protected int status;
protected String statusMessage;
protected void sendHTTPrequest(Map<String,String> map, Map<String, File> fileList) {
Set<String> keys = map.keySet();
status = 0;
response = null;
String boundary = "----" + System.currentTimeMillis();
try {
URL_CONNECTION = BASE_URL.openConnection();
HTTP_CONNECTION = (HttpURLConnection) (URL_CONNECTION);
//Set the request headers
HTTP_CONNECTION.setRequestMethod("POST");
URL_CONNECTION.setRequestProperty("Accept-Charset", CHARSET);
URL_CONNECTION.setRequestProperty("Content-type", "multipart/form-data; boundary=" + boundary);
//Set up a post request
URL_CONNECTION.setDoOutput(true);
OutputStream output = URL_CONNECTION.getOutputStream();
ByteArrayOutputStream bOutput = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, CHARSET), true);
for(String key : keys) {
writer.write("--" + boundary);
writer.write(lineFeed);
writer.write("Content-Disposition: form-data; name=\"" + key + "\"");
writer.write(lineFeed);
writer.write(lineFeed);
writer.write(map.get(key));
writer.write(lineFeed);
}
FileInputStream inputStream;
for(Map.Entry<String, File> entry : fileList.entrySet()){
String name = entry.getKey();
writer.write("--" + boundary);
writer.write(lineFeed);
writer.write("Content-Disposition: form-data; "
+ "name=\"allfiles\"; filename=\"" + name + "\"");
writer.write(lineFeed);
String contentType = URLConnection.guessContentTypeFromName(name);
writer.write("Content-Type: " + contentType);
writer.write(lineFeed);
writer.write("Content-Transfer-Encoding: binary");
writer.write(lineFeed);
writer.write("Content-Id: <" + boundary + ">");
writer.write(lineFeed);
writer.write(lineFeed);
File temp = entry.getValue();
byte[] buffer = FileUtils.readFileToByteArray(temp);
output.write(buffer, 0, buffer.length);
output.flush();
writer.write(lineFeed);
}
writer.write("--" + boundary + "--");
writer.write(lineFeed);
writer.flush();
writer.close();
status = HTTP_CONNECTION.getResponseCode();
statusMessage = HTTP_CONNECTION.getResponseMessage();
if(status == SUCCESS) {
response = IOUtils.toString(URL_CONNECTION.getInputStream(), URL_CONNECTION.getContentEncoding());
}
System.out.println("Finished test " + map.get("testMethod") + " Status: " + status);
System.out.println("Response: " + response);
HTTP_CONNECTION.disconnect();
} catch(UnknownServiceException use) {
throw new RuntimeException("Protocol for the output is not supported");
} catch(IOException ioe) {
throw new RuntimeException("Unable to create the output stream");
}
}
//End sendHTTPrequest method code
I see in the output for status, response and statusMessage, that I get a 500 error, null, Internal Server Error.
Looking on the Coldfusion server I see:
SEVERE: Servlet.service() for servlet [CFCServlet] in context with path [/] threw exception
java.io.IOException: Corrupt form data: no leading boundary: %PDF-1.4 != ------1429222349902
at com.oreilly.servlet.multipart.MultipartParser.<init>(MultipartParser.java:182)
...
massUpload can handle different types of files, but is looking specifically for PDFs. So the unit test needs to be able to send different file types through to massUpload, not just PDFs.
Any insight into the problem would be welcome. Thank you.
(From comments)
Have you used a packet sniffer to see what the above actually generates?
Try flushing the writer just before you write the file bytes:
...
byte[] buffer = FileUtils.readFileToByteArray(temp);
writer.flush(); // flush stream here
output.write(buffer, 0, buffer.length);
...
Update:
Just to clarify, the reason for the original error is that even though the code creates a PrintWriter with autoFlush=true, it still does not automatically save text to the underlying OutputStream when write() is invoked. The autoFlush setting only affects these methods:
autoFlush - if true, the println, printf, or format methods
will [automatically] flush the output buffer
As a result, the code writes the file content to the stream before the boundary markers, which creates an invalid HTTP POST. Hence the 500 error. You can verify this using a package sniffer like Fiddler:
Original - (Invalid):
POST http://localhost:8888/test.cfm HTTP/1.1
Accept-Charset: UTF-8
Content-Type: multipart/form-data; boundary=14cd4c75e24
...
Content-Length: 65570
%PDF-1.4
%
2 0 obj <</Type/XObject/ColorSpace/DeviceGray/Subtype/Image/BitsPerComponent 8/Width 612/Length 11876/Height 792/Filter/FlateDecode>>stream
... more binary
--14cd4c75e24
Content-Disposition: form-data; name="allfiles"; filename="file0.pdf"
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-Id: <14cd4c75e24>
--14cd4c75e24--
The solution is to flush the writer before adding the file content. That ensures the boundary markers are added in the correct location, so the generated POST content is valid.
New Code (Valid)
POST http://localhost:8888/test.cfm HTTP/1.1
Accept-Charset: UTF-8
Content-Type: multipart/form-data; boundary=14cd4c91d29
...
Content-Length: 65570
--14cd4c91d29
Content-Disposition: form-data; name="allfiles"; filename="file0.pdf"
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-Id: <14cd4c91d29>
%PDF-1.4
%
2 0 obj <</Type/XObject/ColorSpace/DeviceGray/Subtype/Image/BitsPerComponent 8/Width 612/Length 11876/Height 792/Filter/FlateDecode>>stream
...
As an aside, I believe Content-Id must be unique. So "boundary" probably is not a good choice for the Content-Id value.
I would like to implement a simple web server in java.
The problem is that images are not correctly rendered on the web browser; all I can see, if I go to localhost:8888/image.png, is a white square with exact width, height and weight.
Thank you in advance! :)
Here is the code:
public Http(Socket server) throws IOException {
in = new BufferedReader(new InputStreamReader(server.getInputStream()));
parseHeader(in);
String response = new String();
out = new PrintWriter(server.getOutputStream(), true);
Files f = new Files(getHomePath() + httpRequestedPage);
if(!f.exists) {
// 404 ERROR
} else {
response += "HTTP/1.1 200 OK\r\n";
response += "Date: " + nowDate + "\r\n";
response += "Content-Type: image/png\r\n";
response += "Content-Length: " + res.length() + "\r\n";
response += "Connection: keep-alive\r\n";
response += "\r\n";
response += IOUtils.toString(new FileInputStream(getHomePath() + httpRequestedPage));
}
out.println(response);
in.close();
out.close();
}
EDIT:
Unfortunately it returns the same message.
out = new PrintWriter(server.getOutputStream(), true);
OutputStream out2 = server.getOutputStream();
File file = new File(HttpServer.getHomePath() + httpRequestedPage);
InputStream stream = new FileInputStream(file);
String response = new String();
response += "HTTP/1.1 200 OK\r\n";
response += "Date: " + nowDate + "\r\n";
response += "Content-Type: image/png\r\n";
response += "Content-Length: " + file.length() + "\r\n";
response += "Connection: keep-alive\r\n";
response += "\r\n";
out.println(response);
IOUtils.copy(stream, out2);
out.close();
out2.close();
You are using Write class for rendering the image. Use the OutputStream to write the image. Images are bytes and always byte based streams should be used to render them.
If you are converting bytes into String then you must use Base64 encoding. And on the client side you can specify the image src similar to this "data:image/png;base64," + imageData.
I'm trying to build a small Java app for connecting to an application called CampFire and am running into trouble trying to upload files to the system. The Java code I'm using to upload a file is as follows:
public static String postFile(String requestUri, File f)
{
debug("Running postFile.");
logIn();
debug("Sending File: " + f.getAbsolutePath() + " to " + campFireURL + requestUri);
URL url;
URLConnection conn;
String linebreak = "\r\n";
String boundary = "**********xxx**********";
String twoHyphens = "--";
String result = "";
String request = twoHyphens + boundary + linebreak +
"Content-Disposition: form-data; name=\"upload\"; filename=\"" + f.getName() + "\"" + linebreak +
linebreak +
"";
debug("Request: " + request);
try
{
FileInputStream in = new FileInputStream(f);
auth.resetTries();
Authenticator.setDefault(auth);
// Send data
url = new URL(campFireURL + requestUri);
conn = url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes(request);
int i;
while((i = in.read()) != -1)
{
wr.write(i);
}
wr.writeBytes(linebreak + twoHyphens + boundary + twoHyphens + linebreak);
wr.flush();
wr.close();
in.close();
result = readFromConnection(conn);
}
catch (Exception e)
{
debug(e);
JOptionPane.showMessageDialog(null, "Error running postData: " + e.getMessage(), "HTTP POST Error", JOptionPane.ERROR_MESSAGE);
die();
}
return(result);
}
When I run this with a real file though, I get the following errors...
Running postFile.
Sending File: /home/myuser/Desktop/blah.png to https://blah.campfirenow.com/room/blah/uploads.xml
Request: --**********xxx**********
Content-Disposition: form-data; name="upload"; filename="blah.png"
Server returned HTTP response code: 422 for URL: blah blah
java.io.IOException: Server returned HTTP response code: 422 for URL: blah blah
Any idea's what I'm doing wrong here? I'm fairly new at Java and am wondering if maybe I missed something obvious?
HTTP error 422 means "Unprocessable Entity". After a quick glance I can spot one mistake: a PNG file is a binary file. You've to add Content-Transfer-Encoding: binary to the header of the part.
If it still doesn't work, then you may find the example in the Uploading files section at the bottom of this answer useful.