首页 >> 大全

HttpClient 上传文件接口报错“missing content

2023-10-15 大全 35 作者:考证青年

问题描述:

同事在使用.4.1版本上传文件时,接口报错“ -type ”。找我过来一起分析原因。部分代码如下:

            HttpPut uploadFile = new HttpPut(uri);uploadFile.setHeader("Authorization", "");uploadFile.setHeader("Content-Type", "multipart/form-data");uploadFile.setHeader("Ocp-Apim-Subscription-Key", key);uploadFile.setHeader("Authorization", bearer);//HttpMultipartMode.RFC6532参数的设定是为避免文件名为中文时乱码MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);multipartEntityBuilder.addBinaryBody("file",new FileInputStream(uploadFile), ContentType.APPLICATION_OCTET_STREAM,uploadFile.getName());HttpEntity httpEntity = multipartEntityBuilder.build();httpPut.setEntity(httpEntity);// 设置请求超时RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(2000).build();httpPut.setConfig(requestConfig);// 发送请求CloseableHttpResponse response = httpClient.execute(httpPut);

问题处理:

经过网上一顿分析查找,网上说把

//uploadFile.setHeader("Content-Type", "multipart/form-data");

这行代码注释掉就可以。我们一试,果然接口成功调用。

问题分析:

想必大家都和我有同样的疑问:为什么注释掉那行代码就可以了?

1、是什么?有什么作用?

官方解析:

当-type为/form-data类型时,需要用指定分隔符。所以后面跟的随机数,就是分隔符,后端就是通过解析到的值作为分隔符来分隔参数的。

想必大家和我一样看到这段解析时,也是不太理解。既然是来分割参数的,参数是在里。那么我们来看下.()方法的源码,看看能不能找到答案。

    private final AbstractMultipartForm multipart;@Overridepublic void writeTo(final OutputStream outstream) throws IOException {this.multipart.writeTo(outstream);}

可以看到调用的是m.()

_HttpClient 上传文件接口报错“missing content_HttpClient 上传文件接口报错“missing content

private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");public void writeTo(final OutputStream out) throws IOException {doWriteTo(out, true);}void doWriteTo(final OutputStream out,final boolean writeContent) throws IOException {final ByteArrayBuffer boundaryEncoded = encode(this.charset, this.boundary);for (final FormBodyPart part: getBodyParts()) {writeBytes(TWO_DASHES, out);writeBytes(boundaryEncoded, out);writeBytes(CR_LF, out);//以--boundaryEncoded\r\n为分割符  分割多个文件或键值对formatMultipartHeader(part, out);writeBytes(CR_LF, out);if (writeContent) {part.getBody().writeTo(out);}writeBytes(CR_LF, out);}// 以--boundaryEncoded\r\n为分割符 结束writeBytes(TWO_DASHES, out);writeBytes(boundaryEncoded, out);writeBytes(TWO_DASHES, out);writeBytes(CR_LF, out);}

从上面源码可知作为分割符,将请求的输出流分割开。

就是当http请求-Type为/form-data时,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有-Type来表名文件类型。

由于有隔离,所以/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。

经过上面的分析,我们知道就是在-Type为/form-data的情况下,作为一个随机数分隔符,来实现既可以上传多个文件,也可以上传键值对。

2、那么问题来了,是如何产生的呢?

大家可能会想到,我自己生成一个行不行:

uploadFile.setHeader("Content-Type", "multipart/form-data;----ba77f35b192c8918628309c77e6add06");

遗憾的是不行,又报出新的错误:“the may have been read by ”。

那么最大嫌疑就只能是er.build()方法,是不是这里生成了?我们来验证下。

public HttpEntity build() {return buildEntity();}

可以看到er.build()调用的是er.()

 MultipartFormEntity buildEntity() {String boundaryCopy = boundary;if (boundaryCopy == null && contentType != null) {boundaryCopy = contentType.getParameter("boundary");}if (boundaryCopy == null) {//生成随机数Boundary  boundaryCopy = generateBoundary();}Charset charsetCopy = charset;if (charsetCopy == null && contentType != null) {charsetCopy = contentType.getCharset();}final List paramsList = new ArrayList(2);paramsList.add(new BasicNameValuePair("boundary", boundaryCopy));if (charsetCopy != null) {paramsList.add(new BasicNameValuePair("charset", charsetCopy.name()));}final NameValuePair[] params = paramsList.toArray(new NameValuePair[paramsList.size()]);//contentType 为空  默认设置为multipart/form-data    private final static String DEFAULT_SUBTYPE = "form-data";final ContentType contentTypeCopy = contentType != null ?contentType.withParameters(params) :ContentType.create("multipart/" + DEFAULT_SUBTYPE, params);final List bodyPartsCopy = bodyParts != null ? new ArrayList(bodyParts) :Collections.emptyList();final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;final AbstractMultipartForm form;switch (modeCopy) {case BROWSER_COMPATIBLE:form = new HttpBrowserCompatibleMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);break;case RFC6532:form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);break;default:form = new HttpStrictMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);}return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());}

       /*** The pool of ASCII chars to be used for generating a multipart boundary.*/private final static char[] MULTIPART_CHARS ="-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();//生成一个30到40位长的随机数private String generateBoundary() {final StringBuilder buffer = new StringBuilder();final Random rand = new Random();final int count = rand.nextInt(11) + 30; // a random size from 30 to 40for (int i = 0; i < count; i++) {buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);}return buffer.toString();}

从上面源码可以看到,原来er默认-type为 /form-data,并且会自动帮我们生成一个生成一个30到40位长的随机数。

总结

这就解释的通为什么注释掉

//uploadFile.setHeader("Content-Type", "multipart/form-data");

还能调用成功,因为er帮我们生成了,并用这个分割请求内容,并且默认-type为 /form-data。

所以当自己设置时:

uploadFile.setHeader("Content-Type", "multipart/form-data;----ba77f35b192c8918628309c77e6add06"); 

又和er生成的不一样,一样报错调用不成功。

扩展

“ -type ” 错误其实和网上大家经常遇到的错误“the was no was found”是一样的,应该是不同框架,报错的提示不一样。

以为例:为空的处理在. new (),留个引子待后续继续研究。

  FileItemIteratorImpl(RequestContext ctx)throws FileUploadException, IOException {if (ctx == null) {throw new NullPointerException("ctx parameter");}String contentType = ctx.getContentType();if ((null == contentType)|| (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {throw new InvalidContentTypeException(String.format("the request doesn't contain a %s or %s stream, content type header is %s",MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));}final long requestSize = ((UploadContext) ctx).contentLength();InputStream input; // N.B. this is eventually closed in MultipartStream processingif (sizeMax >= 0) {if (requestSize != -1 && requestSize > sizeMax) {throw new SizeLimitExceededException(String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",Long.valueOf(requestSize), Long.valueOf(sizeMax)),requestSize, sizeMax);}// N.B. this is eventually closed in MultipartStream processinginput = new LimitedInputStream(ctx.getInputStream(), sizeMax) {@Overrideprotected void raiseError(long pSizeMax, long pCount)throws IOException {FileUploadException ex = new SizeLimitExceededException(String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",Long.valueOf(pCount), Long.valueOf(pSizeMax)),pCount, pSizeMax);throw new FileUploadIOException(ex);}};} else {input = ctx.getInputStream();}String charEncoding = headerEncoding;if (charEncoding == null) {charEncoding = ctx.getCharacterEncoding();}boundary = getBoundary(contentType);if (boundary == null) {IOUtils.closeQuietly(input); // avoid possible resource leakthrow new FileUploadException("the request was rejected because no multipart boundary was found");}notifier = new MultipartStream.ProgressNotifier(listener, requestSize);try {multi = new MultipartStream(input, boundary, notifier);} catch (IllegalArgumentException iae) {IOUtils.closeQuietly(input); // avoid possible resource leakthrow new InvalidContentTypeException(String.format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);}multi.setHeaderEncoding(charEncoding);skipPreamble = true;findNextItem();}

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了