我正在尝试使用 Java HTTP POST 请求将图像发送到网站。
我正在使用此处使用的基本代码将文件从Java客户端上传到HTTP服务器:
这是我的修改:
String urlToConnect = "http://localhost:9000/upload";
File fileToUpload = new File("C:\\Users\\joao\\Pictures\\bla.jpg");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
URLConnection connection = new URL(urlToConnect).openConnection();
connection.setDoOutput(true); // This sets request method to POST.
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
PrintWriter writer = null;
try {
writer = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()));
writer.println("--" + boundary);
writer.println("Content-Disposition: form-data; name=\"picture\"; filename=\"bla.jpg\"");
writer.println("Content-Type: image/jpeg");
writer.println();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileToUpload)));
for (String line; (line = reader.readLine()) != null;) {
writer.println(line);
}
} finally {
if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
}
writer.println("--" + boundary + "--");
} finally {
if (writer != null) writer.close();
}
// Connection is lazily executed whenever you request any status.
int responseCode = ((HttpURLConnection) connection).getResponseCode();
System.out.println(responseCode); // Should be 200
我最终得到了 200 响应代码,但图像有错误,如随机颜色,这让我认为这是字符编码中的错误。我尝试像原始示例一样使用 UTF-8,但这只会创建损坏的图像。
我也 100% 确定这不是服务器端问题,因为我可以使用高级休息客户端/邮递员等休息客户端,并且它们可以毫无问题地发送图像。
你能帮我找出问题所在吗?谢谢你。
import java.io.File;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
public class PostFile {
public static void main(String[] args) throws Exception {
HttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
HttpPost httppost = new HttpPost("http://localhost:9000/upload");
File file = new File("C:\\Users\\joao\\Pictures\\bla.jpg");
MultipartEntity mpEntity = new MultipartEntity();
ContentBody cbFile = new FileBody(file, "image/jpeg");
mpEntity.addPart("userfile", cbFile);
httppost.setEntity(mpEntity);
System.out.println("executing request " + httppost.getRequestLine());
HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
System.out.println(response.getStatusLine());
if (resEntity != null) {
System.out.println(EntityUtils.toString(resEntity));
}
if (resEntity != null) {
resEntity.consumeContent();
}
httpclient.getConnectionManager().shutdown();
}
}
使用 HttpClient 来计算此代码。使用稳定的库总是比从头开始处理更好,除非需要以自定义方式处理某些内容。
今天我遇到了同样的问题,我写了一些nodejs服务器只支持两种路由,上传和下载图像。
客户端应该是一个java类,它通过HTTP POST multipart/form-data标准将图像有效负载发送到服务器。
如果您想知道为什么HTTP POST multipart/form-data,请查看这篇文章中 Ciro Santilli 的答案:What does enctype='multipart/form-data' 是什么意思?
幸运的是,我发现了这个很好的示例代码:
它展示了我们如何在没有任何外部库的情况下手动构建多部分http主体的有效负载,从我的角度来看,唯一的限制是它只能处理带有一个文件的多部分主体。
因为我没有 HTML 页面来嗅探生成的 POST 有效负载,所以我使用 python 来生成它并通过 wireshark 嗅探它。
Python3代码:
import requests
posturl = 'http://<server>:<port>/<path>'
files = {'image' : open('<file>', 'rb')}
r = requests.post(posturl, files = files)
请注意:如果我们使用字典从请求库中定义参数files,它会生成一个多部分/表单数据内容。 http://docs.python-requests.org/en/master/user/advanced/#post-multiple-multipart-encoded-files
Wireshark 将一切显示得非常清楚,最后我用这个来发送 java:
HttpURLConnection conn =
(HttpURLConnection) new URL("http://<server>:<port>/<path>")).openConnection();
// some arbitrary text for multitext boundary
// only 7-bit US-ASCII digits max length 70
String boundary_string = "some radom/arbitrary text";
// we want to write out
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary_string);
// now we write out the multipart to the body
OutputStream conn_out = conn.getOutputStream();
BufferedWriter conn_out_writer = new BufferedWriter(new OutputStreamWriter(conn_out));
// write out multitext body based on w3 standard
// https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
conn_out_writer.write("\r\n--" + boundary_string + "\r\n");
conn_out_writer.write("Content-Disposition: form-data; " +
"name=\"image\"; " +
"filename=\""+ <File class instance>.getName() +"\"" +
"\r\n\r\n");
conn_out_writer.flush();
// payload from the file
FileInputStream file_stream = new FileInputStream(<File class instance>);
// write direct to outputstream instance, because we write now bytes and not strings
int read_bytes;
byte[] buffer = new byte[1024];
while((read_bytes = file_stream.read(buffer)) != -1) {
conn_out.write(buffer, 0, read_bytes);
}
conn_out.flush();
// close multipart body
conn_out_writer.write("\r\n--" + boundary_string + "--\r\n");
conn_out_writer.flush();
// close all the streams
conn_out_writer.close();
conn_out.close();
file_stream.close();
// execute and get response code
conn.getResponseCode();
要从 POST 获取响应,只需读取通过 getInputStream() 访问的输入流,代码片段在链接中。
Reader/Writer 类旨在处理文本数据,而图像是二进制的。您需要将文件解释为二进制文件:
FileChannel in = new FileInputStream(fileToUpload).getChannel();
WritableByteChannel out = Channels.newChannel(connection.getOutputStream());
in.transferTo(0, fileToUpload.size(), out)
当然,你还是需要关闭所有打开的资源。
尝试一下:
private DefaultHttpClient mHttpClient;
Context context;
public String error = "";
//Contrutor para que metodos possam ser usados fora de uma activity
public HTTPconector(Context context) {
this.context = context;
}
public HTTPconector() {
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
mHttpClient = new DefaultHttpClient(params);
}
public void FileClientPost(String txtUrl, File file){
try
{
error = "";
HttpPost httppost = new HttpPost(txtUrl);
MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
multipartEntity.addPart("Image", new FileBody(file));
httppost.setEntity(multipartEntity);
mHttpClient.execute(httppost, new PhotoUploadResponseHandler());
}
catch (Exception e)
{
Log.e(HTTPconector.class.getName(), e.getLocalizedMessage(), e);
e.getStackTrace();
error = e.getMessage();
}
}
//Verifica se a rede esta disponível
public boolean isNetworkAvailable() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
// if no network is available networkInfo will be null
// otherwise check if we are connected
if (networkInfo != null && networkInfo.isConnected()) {
return true;
}
return false;
}
public String Get(String txtUrl){
try {
URL url = new URL(txtUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setReadTimeout(10000);
con.setConnectTimeout(15000);
con.setRequestMethod("GET");
con.setDoInput(true);
con.connect();
return readStream(con.getInputStream());
} catch (ProtocolException e) {
e.printStackTrace();
return "ERRO: "+e.getMessage();
} catch (IOException e) {
e.printStackTrace();
return "ERRO: "+e.getMessage();
}
}
public String Post(String txtUrl){
File image;
try {
URL url = new URL(txtUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setDoInput(true);
con.setDoOutput(true);
con.connect();
//con.getOutputStream().write( ("name=" + "aa").getBytes());
return readStream(con.getInputStream());
} catch (ProtocolException e) {
e.printStackTrace();
return "ERRO: "+e.getMessage();
} catch (IOException e) {
e.printStackTrace();
return "ERRO: "+e.getMessage();
}
}
//Usado para fazer conexão com a internet
public String conectar(String u){
String resultServer = "";
try {
URL url = new URL(u);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
resultServer = readStream(con.getInputStream());
} catch (Exception e) {
e.printStackTrace();
resultServer = "ERRO: "+ e.getMessage();
}
Log.i("HTTPMANAGER: ", resultServer);
return resultServer;
}
//Lê o resultado da conexão
private String readStream(InputStream in) {
String serverResult = "";
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
serverResult = reader.toString();
} catch (IOException e) {
e.printStackTrace();
serverResult = "ERRO: "+ e.getMessage();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
serverResult = "ERRO: "+ e.getMessage();
}
}
}
return serverResult;
}
private class PhotoUploadResponseHandler implements ResponseHandler<Object>
{
@Override
public Object handleResponse(HttpResponse response)throws ClientProtocolException, IOException {
HttpEntity r_entity = response.getEntity();
String responseString = EntityUtils.toString(r_entity);
Log.d("UPLOAD", responseString);
return null;
}
}
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
public void fileClientPost(){
HttpClient httpClient = HttpClientBuilder.create().build();
BasicHttpContext localContext = new BasicHttpContext();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("...", "...");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
HttpEntity entity = (HttpEntity) builder.addPart("file", new FileBody(new File("image.jpg")));
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost, localContext);
}
以下代码适用于 Kotlin。安卓 11+。
Fragment 中的调用如下所示:
val formData = mapOf(
"logdata" to "7780456",
"photo" to encodedBitmap,
}
val url = "https://www.your_server-com/your_php_code.php"
GlobalScope.launch(Dispatchers.Main) {
var returnText = httpHelper.sendFormData(url, formData)
showToast(requireContext(), returnText)
}
HttpHelper 类内部:
suspend fun sendFormData(url: String, formData: Map<String, String>): String {
return withContext(Dispatchers.IO) {
var result = ""
try {
val connection = URL(url).openConnection() as HttpURLConnection
connection.connectTimeout = 10000 // 10 seconds
connection.readTimeout = 20000 // 20 seconds
connection.requestMethod = "POST"
connection.doOutput = true
connection.doInput = true
connection.useCaches = false;
val boundary = "*****"
connection.setRequestProperty("Connection", "Keep-Alive")
connection.setRequestProperty("Cache-Control", "no-cache");
connection.setRequestProperty(
"Content-Type",
"multipart/form-data;boundary=$boundary"
)
val outputStream = connection.outputStream
val writer = PrintWriter(OutputStreamWriter(outputStream, "UTF-8"), false)
val os = DataOutputStream(outputStream)
// Write form data
for ((key, value) in formData) {
if (key != "photo") {
writer.write("--$boundary\r\n")
writer.write("Content-Disposition: form-data; name=\"$key\"\r\n")
writer.write("\r\n")
writer.write(value)
writer.write("\r\n")
}
}
// Write image data
val imageKey = "photo"
if (formData.containsKey(imageKey)) {
writer.write("--$boundary\r\n")
writer.write("Content-Disposition: form-data; name=\"$imageKey\"; filename=\"photo.jpg\"\r\n")
writer.write("Content-Type: image/jpeg\r\n")
writer.write("\r\n")
writer.flush()
val imageData = Base64.decode(formData[imageKey], Base64.DEFAULT)
os.write(imageData)
os.write("\r\n".toByteArray())
}
writer.write("--$boundary--\r\n")
writer.flush()
writer.close()
// Read response
val responseCode = connection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
val inputStream = connection.inputStream
val reader = BufferedReader(InputStreamReader(inputStream))
val response = StringBuffer()
var line: String?
while (reader.readLine().also { line = it } != null) {
response.append(line)
}
reader.close()
connection.disconnect()
result= response.toString()
} else {
// Handle error
// For example: throw an exception or return an error message
result = "Error: HTTP $responseCode"
}
} catch (e: Exception) {
// Handle exceptions
result = "Error: ${e.message}"
}
result
}
}