如何使用Java将多部分/表单数据发布上传到Nexus存储库?
我的目标是通过Java将文件上传到Nexus。如果您不熟悉NexusRESTAPI,我需要使用多部分/表单数据内容发布帖子,以便发送Nexus字段(如我的文件所属目录)和我的文件内容 简单的解决方案是使用Apache组件工具集中的MultipartEntityBuilder,但由于项目限制,我无法访问Apache HttpClient Mime库(尽管我可以访问所有核心HTTP内容)。所以我必须做些别的事情。同样由于项目限制,我只能访问内置Java库和大多数Apache组件工具集,但我没有任何其他HTTP库(如okhttp、Jersey、Restlet等) 我的代码没有中断,但我能从Nexus得到的只是HTTP错误代码。我可以从Nexus获得响应头,但我无法获得Nexus接受的配置。我尝试过在Firefox的网络控制台工具中手动发布,它可以正常工作并上传一个文件(只要我有一个有效的cookie进行身份验证)。所以我知道这不是真正的身体被错误的格式化,因为如果我手动的话它会工作。问题是在我的HttpClient设置中添加了一些内容或配置错误,因此Nexus无法理解我的意思 到目前为止,我得到的是:如何使用Java将多部分/表单数据发布上传到Nexus存储库?,java,http,post,nexus,apache-commons-httpclient,Java,Http,Post,Nexus,Apache Commons Httpclient,我的目标是通过Java将文件上传到Nexus。如果您不熟悉NexusRESTAPI,我需要使用多部分/表单数据内容发布帖子,以便发送Nexus字段(如我的文件所属目录)和我的文件内容 简单的解决方案是使用Apache组件工具集中的MultipartEntityBuilder,但由于项目限制,我无法访问Apache HttpClient Mime库(尽管我可以访问所有核心HTTP内容)。所以我必须做些别的事情。同样由于项目限制,我只能访问内置Java库和大多数Apache组件工具集,但我没有任何其
CloseableHttpClient clientConnection = ConnectionCommon.getCloseableHttpClient(ssl);
/*
* getCloseableClient is a custom function which returns a client with
* proper SSL configuration set up depending on what's needed.
*/
HttpPost post = new HttpPost(ConnectionCommon.constructURL(schema, host, portNum, dataType, repo));
String formBody = getFormBody();
/* Create the body entity.
* I've tried various version of this, and none have worked. I wanted to try
* creating my own ContentType but I couldn't due to it being a final class.
*/
post.setEntity(new StringEntity(formBody));
// post.setEntity(new EntityBuilder.create().setText(formBody).build());
post.addHeader(new BasicHeader("Accept", "application/json"));
post.addHeader(new BasicHeader("Content-Type", "multipart/form-data; boundary=" + generatedBoundary));
// Get the server response, after generating our authentication context (if needed).
CloseableHttpResponse serverResponse = getCloseableResponse(clientConnection, post, auth);
// Process output and stuff here...
如果我这样做:
post.setEntity(new StringEntity(formBody));
post.addHeader(new BasicHeader("Accept", "application/json"));
post.addHeader(new BasicHeader("Content-Type", "multipart/form-data; boundary=" + generatedBoundary));
或:
我得到了一个422:Unprocessable Entity
错误,Nexus没有提供关于错误格式的其他信息。响应主体为空
我想可能是setEntity
为我添加了一个内容类型multipart/form数据,因为我在标题中添加了另一个,所以很混乱。所以我试着:
post.setEntity(new StringEntity(formBody);
post.addHeader(new BasicHeader("Accept", "application/json");
这给了我415:不支持的媒体类型
。再次没有回应机构。我有点期待这种情况,因为Nexus服务器拒绝您发布的任何内容,除非是多部分/表单数据
因此,接下来我尝试让EntityBuilder生成标题:
post.setEntity(EntityBuilder.create().setText(formBody).
setContentType(ContentType.MULTIPART_FORM_DATA).build());
post.addHeader(new BasicHeader("Accept", "application/json"));
但是我得到了500:internalserver Error
,响应头说:
java.io.IOException:无法获取多部分的边界
好吧,至少这很简单。显然,我必须给它一个界限。但是这样做会给我上面提到的422
好吧,让我传入边界,作为EntityBuilder MIME的一部分:
post.setEntity(EntityBuilder.create().setText(formBody).
setContentType(ContentType.create("multipart/form-data; boundary=" + generatedBoundary, Consts.UTF_8)).build());
post.addHeader(new BasicHeader("Accept", "application/json"));
但它甚至没有运行,因为事实证明,“;”是传递到ContentType的“无效”字符
java.lang.IllegalArgumentException:MIME类型不能包含保留字符
好的,那么,我尝试在没有多部分过程的情况下,将边界本身添加到标题中(我没有这样做,只是为了工作,因为必须一起指定它们):
正如预期的那样,这没有起作用。令人惊讶的是,我得到了一个实际的html页面作为我的响应主体,但它只是说400:Bad response
好吧,我说“去他妈的”。然后,我尝试使用Java的HTTP内容,并创建一个多部分编写器来手工编写
我找到了一个用户的答案,我基本上是逐字复制来测试的。我将把下面的代码留给后代,但我自己并没有想出这个解决方案
public class MultipartUtility {
private final String boundary;
private static final String LINE_FEED = "\r\n";
private HttpURLConnection httpConn;
private String charset;
private OutputStream outputStream;
private PrintWriter writer;
/**
* This constructor initializes a new HTTP POST request with content type
* is set to multipart/form-data
*
* @param requestURL
* @param charset
* @throws IOException
*/
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);
outputStream = httpConn.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
true);
}
/**
* Adds a form field to the request
*
* @param name field name
* @param value field value
*/
public void addFormField(String name, String value) {
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
.append(LINE_FEED);
writer.append("Content-Type: text/plain; charset=" + charset).append(
LINE_FEED);
writer.append(LINE_FEED);
writer.append(value).append(LINE_FEED);
writer.flush();
}
/**
* Adds a upload file section to the request
*
* @param fieldName name attribute in <input type="file" name="..." />
* @param uploadFile a File to be uploaded
* @throws IOException
*/
public void addFilePart(String fieldName, File uploadFile)
throws IOException {
String fileName = uploadFile.getName();
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(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();
}
/**
* Adds a header field to the request.
*
* @param name - name of the header field
* @param value - value of the header field
*/
public void addHeaderField(String name, String value) {
writer.append(name + ": " + value).append(LINE_FEED);
writer.flush();
}
/**
* Completes the request and receives response from the server.
*
* @return a list of Strings as response in case the server returned
* status OK, otherwise an exception is thrown.
* @throws IOException
*/
public List<String> finish() throws IOException {
List<String> response = new ArrayList<String>();
writer.append(LINE_FEED).flush();
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.close();
// checks server's status code first
int status = httpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(
httpConn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
response.add(line);
}
reader.close();
httpConn.disconnect();
} else {
throw new IOException("Server returned non-OK status: " + status);
}
return response;
}
}
编辑:我已经通过Python脚本很好地实现了这一点,所以我知道这不可能是服务器问题。唯一的问题是我必须将这个上传步骤作为现有Java程序的一部分进行集成,所以我不能只制作一个两行Python脚本
因此,现在我恳求Stack Overflow社区看看是否有人能提供帮助
post.setEntity(new StringEntity(formBody);
post.addHeader(new BasicHeader("Accept", "application/json");
post.addHeader(new BasicHeader("Content-Type", "multipart/form-data; boundary=" + generatedBoundary));
public class MultipartUtility {
private final String boundary;
private static final String LINE_FEED = "\r\n";
private HttpURLConnection httpConn;
private String charset;
private OutputStream outputStream;
private PrintWriter writer;
/**
* This constructor initializes a new HTTP POST request with content type
* is set to multipart/form-data
*
* @param requestURL
* @param charset
* @throws IOException
*/
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);
outputStream = httpConn.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
true);
}
/**
* Adds a form field to the request
*
* @param name field name
* @param value field value
*/
public void addFormField(String name, String value) {
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
.append(LINE_FEED);
writer.append("Content-Type: text/plain; charset=" + charset).append(
LINE_FEED);
writer.append(LINE_FEED);
writer.append(value).append(LINE_FEED);
writer.flush();
}
/**
* Adds a upload file section to the request
*
* @param fieldName name attribute in <input type="file" name="..." />
* @param uploadFile a File to be uploaded
* @throws IOException
*/
public void addFilePart(String fieldName, File uploadFile)
throws IOException {
String fileName = uploadFile.getName();
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(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();
}
/**
* Adds a header field to the request.
*
* @param name - name of the header field
* @param value - value of the header field
*/
public void addHeaderField(String name, String value) {
writer.append(name + ": " + value).append(LINE_FEED);
writer.flush();
}
/**
* Completes the request and receives response from the server.
*
* @return a list of Strings as response in case the server returned
* status OK, otherwise an exception is thrown.
* @throws IOException
*/
public List<String> finish() throws IOException {
List<String> response = new ArrayList<String>();
writer.append(LINE_FEED).flush();
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.close();
// checks server's status code first
int status = httpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(
httpConn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
response.add(line);
}
reader.close();
httpConn.disconnect();
} else {
throw new IOException("Server returned non-OK status: " + status);
}
return response;
}
}
--$$$010678954$$$
Content-Disposition: form-data; name="raw.directory"
PleaseWork
--$$$010678954$$$
Content-Disposition: form-data; name="raw.asset1"; filename="please.txt"
Content-Type: text/plain
Test test test
Foobar test
Random file contents blah
--$$$010678954$$$
Content-Disposition: form-data; name="raw.asset1.filename"
PleaseWorkPlease.txt
--$$$010678954$$$--