英文:
Android HttpUrlConneion upload a large file cause 413, but getResponseCode can't return and throw another exception
问题
在我的应用程序中,我使用HttpUrlConnection将文件上传到服务器。当我尝试上传一个大文件时,nginx返回一个413异常代码,但是HttpUrlConnection的getResponseCode方法无法捕获异常,我得到了另一个异常消息。
以下是异常消息:
错误 asyncExecute: 写入错误:ssl=0x7ec7b80008:系统调用期间的I/O错误,连接被对等方重置
以下是我的代码:
URL originUrl = new URL(url);
String protocol = originUrl.getProtocol();
if (TextUtils.equals(protocol, "http")) {
port = 80;
}
mURL = new URL(protocol, originUrl.getHost(), port, originUrl.getFile());
mConn = (HttpURLConnection) mURL.openConnection();
if (mConn instanceof HttpsURLConnection) {
selfSignedCertificate = true;
SSLCustomSocketFactory factory = new SSLCustomSocketFactory(selfSignedCertificate ? SSLCustomSocketFactory.getSocketFactory()
: (SSLSocketFactory) SSLSocketFactory.getDefault());
((HttpsURLConnection) mConn).setSSLSocketFactory(factory);
((HttpsURLConnection) mConn).setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
mConn.setRequestMethod("POST");
mConn.setDoOutput(true);
mConn.setDoInput(true);
mConn.setUseCaches(false);
mConn.setConnectTimeout(30000);
mConn.setReadTimeout(30000);
mConn.setRequestProperty("User-agent", "xxxx");//xxxx涉及到项目中信息不展示
mConn.setRequestProperty("Connection", "Keep-Alive");
mConn.setRequestProperty("Charset", "UTF-8");
mConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
mConn.setRequestProperty("Expect", "100-Continue");
mConn.setChunkedStreamingMode(0);
//request headers
for (Map.Entry<String, String> item : headers.entrySet()) {
mConn.setRequestProperty(item.getKey(), item.getValue());
}
mConn.connect();
//get outputStream
DataOutputStream out = new DataOutputStream(connect.getOutputStream());
//write params
out.write(params.getBytes());
out.flush();
StringBuffer strBuf = new StringBuffer();
for (String key : paramsMap.keySet()){
strBuf.append(TWO_HYPHENS);
strBuf.append(BOUNDARY);
strBuf.append(LINE_END);
strBuf.append("Content-Disposition: form-data; name=\"" + key + "\"");
strBuf.append(LINE_END);
strBuf.append("Content-Type: " + "text/plain" );
strBuf.append(LINE_END);
strBuf.append("Content-Length: "+paramsMap.get(key).length());
strBuf.append(LINE_END);
strBuf.append(LINE_END);
strBuf.append(paramsMap.get(key));
strBuf.append(LINE_END);
}
String paramsString = strBuf.toString();
out.write(paramsString.getBytes());
out.flush();
String fileName = UriUtils.getFileNameByUri(mContext, fileUri);
String mimeType = UriUtils.getMimeType(mContext, fileUri);
long fileLength = UriUtils.getFileLength(mContext, fileUri);
if(!TextUtils.isEmpty(filename)) {
fileName = filename;
}
//add file headers
out.write(getFileHeaderParamsString(fileKey, fileName, mimeType, fileLength).getBytes());
//add file body
String filePath = UriUtils.getFilePath(fileUri);
InputStream in = null;
try {
if (!TextUtils.isEmpty(filePath) && new File(filePath).exists()) {
in = new FileInputStream(new File(filePath));
} else {
in = mContext.getContentResolver().openInputStream(fileUri);
}
byte[] tmp = new byte[2048];
int l;
long sum = 0;
while ((l = in.read(tmp)) != -1) {
out.write(tmp, 0, l);
sum += l;
if (callback != null) {
callback.onProgress(fileLength, sum);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
}
//add file end
out.write(getFileEndString().getBytes());
out.flush();
HttpResponse response = new HttpResponse();
//can't get response code here
response.code = mConn.getResponseCode();
if(response.code == HttpURLConnection.HTTP_OK) {
response.contentLength = mConn.getContentLength();
response.inputStream = mConn.getInputStream();
response.content = parseStream(response.inputStream);
}else {
response.errorStream = mConn.getErrorStream();
response.content = parseStream(response.errorStream);
}
......
public static class SSLCustomSocketFactory extends SSLSocketFactory {
private static final String TAG = "SSLCustomSocketFactory";
private static final String[] TLS_SUPPORT_VERSION = {"TLSv1.1", "TLSv1.2"};
private static final String KEY_PASS = "";
final SSLSocketFactory delegate;
static SSLContext sslContext;
static SSLSocketFactory mSocketFactory;
public SSLCustomSocketFactory(SSLSocketFactory base) {
delegate = base;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return patch(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if(s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_SUPPORT_VERSION);
}
return s;
}
public static synchronized SSLSocketFactory getSocketFactory() {
if(mSocketFactory == null) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
AssetManager am = context().getAssets();
String[] certsPaths = am.list("xxx_certs");
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null
<details>
<summary>英文:</summary>
In my application I use HttpUrlConnection to upload file to server, when I want to upload a large file, nginx give me a 413 exception code, but HttpUrlConnection's getResponseCode can't catch the exception, I got another exception message.
Here is the exception message:
error asyncExecute:Write error: ssl=0x7ec7b80008: I/O error during system call, Connection reset by peer
Here is my code:
URL originUrl = new URL(url);
String protocol = originUrl.getProtocol();
if (TextUtils.equals(protocol, "http")) {
port = 80;
}
mURL = new URL(protocol, originUrl.getHost(), port, originUrl.getFile());
mConn = (HttpURLConnection) mURL.openConnection();
if (mConn instanceof HttpsURLConnection) {
selfSignedCertificate = true;
SSLCustomSocketFactory factory = new SSLCustomSocketFactory(selfSignedCertificate ? SSLCustomSocketFactory.getSocketFactory()
: (SSLSocketFactory) SSLSocketFactory.getDefault());
((HttpsURLConnection) mConn).setSSLSocketFactory(factory);
((HttpsURLConnection) mConn).setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
mConn.setRequestMethod("POST");
mConn.setDoOutput(true);
mConn.setDoInput(true);
mConn.setUseCaches(false);
mConn.setConnectTimeout(30000);
mConn.setReadTimeout(30000);
mConn.setRequestProperty("User-agent", "xxxx");//xxxx涉及到项目中信息不展示
mConn.setRequestProperty("Connection", "Keep-Alive");
mConn.setRequestProperty("Charset", "UTF-8");
mConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
mConn.setRequestProperty("Expect", "100-Continue");
mConn.setChunkedStreamingMode(0);
//request headers
for (Map.Entry<String, String> item : headers.entrySet()) {
mConn.setRequestProperty(item.getKey(), item.getValue());
}
mConn.connect();
//get outputStream
DataOutputStream out = new DataOutputStream(connect.getOutputStream());
//write params
out.write(params.getBytes());
out.flush();
StringBuffer strBuf = new StringBuffer();
for (String key : paramsMap.keySet()){
strBuf.append(TWO_HYPHENS);
strBuf.append(BOUNDARY);
strBuf.append(LINE_END);
strBuf.append("Content-Disposition: form-data; name=\"" + key + "\"");
strBuf.append(LINE_END);
strBuf.append("Content-Type: " + "text/plain" );
strBuf.append(LINE_END);
strBuf.append("Content-Length: "+paramsMap.get(key).length());
strBuf.append(LINE_END);
strBuf.append(LINE_END);
strBuf.append(paramsMap.get(key));
strBuf.append(LINE_END);
}
String paramsString = strBuf.toString();
out.write(paramsString.getBytes());
out.flush();
String fileName = UriUtils.getFileNameByUri(mContext, fileUri);
String mimeType = UriUtils.getMimeType(mContext, fileUri);
long fileLength = UriUtils.getFileLength(mContext, fileUri);
if(!TextUtils.isEmpty(filename)) {
fileName = filename;
}
//add file headers
out.write(getFileHeaderParamsString(fileKey, fileName, mimeType, fileLength).getBytes());
//add file body
String filePath = UriUtils.getFilePath(fileUri);
InputStream in = null;
try {
if (!TextUtils.isEmpty(filePath) && new File(filePath).exists()) {
in = new FileInputStream(new File(filePath));
} else {
in = mContext.getContentResolver().openInputStream(fileUri);
}
byte[] tmp = new byte[2048];
int l;
long sum = 0;
while ((l = in.read(tmp)) != -1) {
out.write(tmp, 0, l);
sum += l;
if (callback != null) {
callback.onProgress(fileLength, sum);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
}
//add file end
out.write(getFileEndString().getBytes());
out.flush();
HttpResponse response = new HttpResponse();
//can't get response code here
response.code = mConn.getResponseCode();
if(response.code == HttpURLConnection.HTTP_OK) {
response.contentLength = mConn.getContentLength();
response.inputStream = mConn.getInputStream();
response.content = parseStream(response.inputStream);
}else {
response.errorStream = mConn.getErrorStream();
response.content = parseStream(response.errorStream);
}
......
public static class SSLCustomSocketFactory extends SSLSocketFactory {
private static final String TAG = "SSLCustomSocketFactory";
private static final String[] TLS_SUPPORT_VERSION = {"TLSv1.1", "TLSv1.2"};
private static final String KEY_PASS = "";
final SSLSocketFactory delegate;
static SSLContext sslContext;
static SSLSocketFactory mSocketFactory;
public SSLCustomSocketFactory(SSLSocketFactory base) {
delegate = base;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return patch(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if(s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_SUPPORT_VERSION);
}
return s;
}
public static synchronized SSLSocketFactory getSocketFactory() {
if(mSocketFactory == null) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
AssetManager am = context().getAssets();
String[] certsPaths = am.list("xxx_certs");
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
for (int i = 0; i < certsPaths.length; i++) {
String certPath = certsPaths[i];
InputStream caInput = null;
try {
caInput = am.open("xxxx_certs/" + certPath);
Certificate ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
keyStore.setCertificateEntry("ca" + i, ca);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (caInput != null) {
caInput.close();
}
}
}
sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
sslContext.init(null, tmf.getTrustManagers(), null);
mSocketFactory = sslContext.getSocketFactory();
} catch (Throwable e) {
e.printStackTrace();
}
}
return mSocketFactory;
}
}
Here is the nagix return:
<html>
<head>
<title>413 Request Entity Too Large</title>
</head>
<body>
<center>
<h1>413 Request Entity Too Large</h1>
</center>
<hr>
<center>nginx/1.17.5</center>
</body>
<html>
How I can get the right exception code and exception message?
</details>
# 答案1
**得分**: 0
这是服务器确定的有效载荷限制,您的应用程序无法对此有效载荷进行任何操作以接受它。您必须验证发送的格式和数据量是否正确。
如果服务器是您自己的,您必须更改此限制。请参考这个链接:https://stackoverflow.com/questions/26717013/how-to-edit-nginx-conf-to-increase-file-size-upload
<details>
<summary>英文:</summary>
This is a payload limit determined by the server, there is nothing you can do in your application to accept this payload. You must verify that the format and the amount of data sent is correct.
If the server is yours, you must change the limit. Look at this: https://stackoverflow.com/questions/26717013/how-to-edit-nginx-conf-to-increase-file-size-upload
</details>
专注分享java语言的经验与见解,让所有开发者获益!
评论