Sending multiple files in one post Request in HttpURLConnection in JAVA - java

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.

Related

How to write a file to an HTTP Request Java

I am new to using HTTP and I have questions about writing a file and another value to an HTTP Post request in Java. I am using an public API provided by a company called Mojang to write what is known as a "skin" (a png file) to the game Minecraft for player character modles. Here is the documentation of how to use this public API for reference:https://wiki.vg/Mojang_API#Upload_Skin
Here is the code I have written. When ran I get the 415 HTTP Response code (which I assume is "unsupported media type"). Any suggestions on what I am doing wrong and how I can fix this? I found other stack overflow issues for uploading files but I need to also add a value called "variant={classic or slim}". I am a little lost on how to make all of this work. Any help is much appreciated.
(I could not get the code to properally format in the code sample using ' ', it is in a javascript snippet)
public static void uploadSkin(String accessToken, String variant, File file) throws IOException {
URL url = new URL("https://api.minecraftservices.com/minecraft/profile/skins");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Authorization", "Bearer " + accessToken); // The access token is provided after an
// authentication request has been send, I
// have done this sucessfully in another
// method and am passing it in here
con.addRequestProperty("variant", variant);
OutputStream outputStream = con.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(con.getOutputStream(), "utf-8"), true);
String boundary = "===" + System.currentTimeMillis() + "===";
String fileName = file.getName();
String LINE_FEED = "\r\n";
String fieldName = "file";
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"")
.append(LINE_FEED);
writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
FileInputStream inputStream = new FileInputStream(file);
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();
}
Alright, found a solution to the problem. Using this maven dependency:
<!-- https://mvnrepository.com/artifact/org.jodd/jodd-http -->
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<version>5.0.2</version>
</dependency>
And then this:
HttpResponse response = HttpRequest.post("https://api.minecraftservices.com/minecraft/profile/skins")
.header("Authorization", "Bearer " + accessToken).header("Content-Type", "multipart/form-data")
.form("variant", variant).form("file", file).send();
I was able to get it to work. Hope this is helpful to anyone that needs to upload a Skin Png file to Minecraft.

Server not running body multipart/form-data

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();
}

HttpURLConnection is Connection reset or Broken pipe

I am trying to upload byte array to http server using post method from android. But i am getting always connection reset or Broken pipe error. Could pls help me understand where i am doing wrong.
int bufferBytes = 131072; // 128 KB
Random r = new Random( System.currentTimeMillis() );
int fileName = 10000 + r.nextInt(20000);
String urlParameters = ""+ StringUtils.leftPad("abcdefgh", bufferBytes, "abcdefgh");
byte[] postData = urlParameters.getBytes( StandardCharsets.UTF_8 );
String CRLF = "\r\n";
String boundary = "-----------------------******";
OutputStream out = null;
HttpURLConnection urlConnection = null;
PrintWriter writer = null;
try {
URL url = new URL("host");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setConnectTimeout(10*1000) ; // 15 Sec time out
System.setProperty("http.keepAlive", "false");
urlConnection.setChunkedStreamingMode(1024);
//urlConnection.setRequestProperty("Keep-Alive", "false");
urlConnection.setRequestProperty("connection", "close");
urlConnection.setUseCaches (false);
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Accept", "text/xml,application/x ml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
urlConnection.setConnectTimeout(15*1000) ; // 15 Sec time out
urlConnection.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
urlConnection.setRequestProperty("Accept-Encoding", "identity");
urlConnection.setRequestProperty("Transfer-Encoding", "chunked") ;
urlConnection.connect();
out = urlConnection.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true);
// Send normal param.
writer.append("100000").append(CRLF);
writer.append(boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"success_action_status\"").append(CRLF);
writer.append("201").append(CRLF);
writer.append(boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"Content-Type\"").append(CRLF);
writer.append("image/jpeg").append(CRLF);
writer.append(boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"x-amz-acl\"").append(CRLF);
writer.append("public-read").append(CRLF);
writer.append(boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"key\"").append(CRLF);
writer.append("images/237615.jpg").append(CRLF);
writer.append(boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"FileName\"").append(CRLF);
writer.append("ul+"+fileName+".jpg").append(CRLF);
writer.append(boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"file\"; filename=ul" +fileName).append(CRLF);
writer.append("Content-Type: image/jpeg").append(CRLF); // Text file itself must be saved in this charset!
writer.append(CRLF).flush();
out.flush(); // Important before continuing with writer!
writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
long startTime = System.currentTimeMillis();
while (true){
out.write(postData);
out.flush();
if(System.currentTimeMillis() - startTime > 10000)
break;
}
Log.d("Upload" , " Upload is success");
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}finally {
try {
if(writer != null)
writer.close();
if(out != null)
out.close();
if(urlConnection != null)
urlConnection.disconnect();
}catch (Exception e){
e.printStackTrace();
}
}
If i comment the below line i am getting the java.net.SocketException: Connection reset else i am getting the java.net.SocketException: Broken pipe
System.setProperty("http.keepAlive", "false");
Don't set the content length. It isn't required at all in chunked transfer mode, and it isn't required at all in Java HttpURLConnection, which sets it for you when necessary.

Unable to send multipart post, using java, to node.js API server

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

Send image with HttpUrlConnection by chunks and other parameters

The thing is I'm trying to upload an image to a server.
The image has to be uploaded by chunks of 256kb and I need to pass the chunks count and id with every call.
I can get the total number of chunks to upload and I'm using a BufferedInputStream to get the chunks bytes.
But when I finish to upload all the chunks the image showed is always corrupted.
My code so far:
int chunkSize = 255 * 1024;
final long size = mFile.length();
final long chunks = mFile.length() < chunkSize? 1: (mFile.length() / chunkSize);
int chunkId = 0;
BufferedInputStream stream = new BufferedInputStream(new FileInputStream(mFile));
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "RQdzAAihJq7Xp1kjraqf";// random data
for (chunkId = 0; chunkId < chunks; chunkId++) {
URL url = new URL(urlString);
// Open a HTTP connection to the URL
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(20000 /* milliseconds */);
conn.setConnectTimeout(20000 /* milliseconds */);
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);
dos = new DataOutputStream( conn.getOutputStream() );
dos.writeBytes(twoHyphens + boundary + lineEnd);
String param1 = ""+chunkId;
String param2 = ""+chunks;
String param3 = mFile.getName();
// for every param
dos.writeBytes("Content-Disposition: form-data; name=\"chunk\"" + lineEnd);
dos.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dos.writeBytes("Content-Length: " + param1.length() + lineEnd);
dos.writeBytes(lineEnd);
dos.writeBytes(param1 + lineEnd);
dos.writeBytes(twoHyphens + boundary + lineEnd);
// Send parameter #chunks
dos.writeBytes("Content-Disposition: form-data; name=\"chunks\"" + lineEnd);
dos.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dos.writeBytes("Content-Length: " + param2.length() + lineEnd);
dos.writeBytes(lineEnd);
dos.writeBytes(param2 + lineEnd);
dos.writeBytes(twoHyphens + boundary + lineEnd);
// Send parameter #name
dos.writeBytes("Content-Disposition: form-data; name=\"name\"" + lineEnd);
dos.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dos.writeBytes("Content-Length: " + param4.length() + lineEnd);
dos.writeBytes(lineEnd);
dos.writeBytes(param3 + lineEnd);
dos.writeBytes(twoHyphens + boundary + lineEnd);
// Send parameter #file
dos.writeBytes("Content-Disposition: form-data; name=\"file\";filename=\"" + param4 + "\"" + lineEnd); // filename is the Name of the File to be uploaded
dos.writeBytes("Content-Type: image/jpeg" + lineEnd);
dos.writeBytes(lineEnd);
byte[] buffer = new byte[chunkSize];
stream.skip(chunkId * chunkSize);
stream.read(buffer);
// dos.write(buffer, 0, bufferSize);
dos.write(buffer);
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
dos.flush();
dos.close();
// read response...
}
Many thanks!
Well,
I solved the issue.
I deleted the following line:
stream.skip(chunkId * chunkSize);
I was skipping several chunks of the stream :). Sorry my bad.
You have to define "multipart/form-data" instead of form-data when sending image (file)

Categories

Resources