media
This commit is contained in:
parent
f0dfffe9bb
commit
3779c33046
@ -2,10 +2,17 @@ package com.foxinmy.weixin4j.http;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.util.CharArrayBuffer;
|
||||||
import com.foxinmy.weixin4j.util.Consts;
|
import com.foxinmy.weixin4j.util.Consts;
|
||||||
|
import com.foxinmy.weixin4j.util.NameValue;
|
||||||
|
import com.foxinmy.weixin4j.util.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reference of apache pivot
|
* reference of apache pivot
|
||||||
@ -22,6 +29,7 @@ public final class ContentType implements Serializable {
|
|||||||
|
|
||||||
private final MimeType mimeType;
|
private final MimeType mimeType;
|
||||||
private final Charset charset;
|
private final Charset charset;
|
||||||
|
private final NameValue[] params;
|
||||||
private static final Charset DEFAULT_CHARSET = Consts.UTF_8;
|
private static final Charset DEFAULT_CHARSET = Consts.UTF_8;
|
||||||
|
|
||||||
public static final ContentType APPLICATION_JSON;
|
public static final ContentType APPLICATION_JSON;
|
||||||
@ -32,7 +40,8 @@ public final class ContentType implements Serializable {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
APPLICATION_JSON = new ContentType(MimeType.APPLICATION_JSON);
|
APPLICATION_JSON = new ContentType(MimeType.APPLICATION_JSON);
|
||||||
APPLICATION_FORM_URLENCODED = new ContentType(MimeType.APPLICATION_FORM_URLENCODED);
|
APPLICATION_FORM_URLENCODED = new ContentType(
|
||||||
|
MimeType.APPLICATION_FORM_URLENCODED);
|
||||||
MULTIPART_FORM_DATA = new ContentType(MimeType.MULTIPART_FORM_DATA);
|
MULTIPART_FORM_DATA = new ContentType(MimeType.MULTIPART_FORM_DATA);
|
||||||
DEFAULT_BINARY = new ContentType(MimeType.APPLICATION_OCTET_STREAM);
|
DEFAULT_BINARY = new ContentType(MimeType.APPLICATION_OCTET_STREAM);
|
||||||
DEFAULT_TEXT = new ContentType(MimeType.TEXT_PLAIN);
|
DEFAULT_TEXT = new ContentType(MimeType.TEXT_PLAIN);
|
||||||
@ -43,8 +52,14 @@ public final class ContentType implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContentType(final MimeType mimeType, final Charset charset) {
|
ContentType(final MimeType mimeType, final Charset charset) {
|
||||||
|
this(mimeType, charset, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentType(final MimeType mimeType, final Charset charset,
|
||||||
|
final NameValue[] params) {
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.charset = charset;
|
this.charset = charset;
|
||||||
|
this.params = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MimeType getMimeType() {
|
public MimeType getMimeType() {
|
||||||
@ -55,11 +70,34 @@ public final class ContentType implements Serializable {
|
|||||||
return this.charset;
|
return this.charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public String getParameter(final String name) {
|
||||||
|
if (this.params == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (final NameValue param : this.params) {
|
||||||
|
if (param.getName().equalsIgnoreCase(name)) {
|
||||||
|
return param.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates textual representation of this content type which can be used
|
||||||
|
* as the value of a {@code Content-Type} header.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder();
|
final CharArrayBuffer buf = new CharArrayBuffer(64);
|
||||||
buf.append(this.mimeType.toString());
|
buf.append(this.mimeType);
|
||||||
if (this.charset != null) {
|
if (this.params != null) {
|
||||||
|
buf.append("; ");
|
||||||
|
HeaderValueFormatter.INSTANCE.formatParameters(buf,
|
||||||
|
this.params, false);
|
||||||
|
} else if (this.charset != null) {
|
||||||
buf.append("; charset=");
|
buf.append("; charset=");
|
||||||
buf.append(this.charset.name());
|
buf.append(this.charset.name());
|
||||||
}
|
}
|
||||||
@ -87,7 +125,8 @@ public final class ContentType implements Serializable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ContentType create(final MimeType mimeType, final Charset charset) {
|
public static ContentType create(final MimeType mimeType,
|
||||||
|
final Charset charset) {
|
||||||
if (mimeType == null) {
|
if (mimeType == null) {
|
||||||
throw new IllegalArgumentException("MIME type may not be null");
|
throw new IllegalArgumentException("MIME type may not be null");
|
||||||
}
|
}
|
||||||
@ -98,7 +137,16 @@ public final class ContentType implements Serializable {
|
|||||||
return create(MimeType.valueOf(mimeType), (Charset) null);
|
return create(MimeType.valueOf(mimeType), (Charset) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ContentType create(final String mimeType, final Charset charset) {
|
public static ContentType create(final String mimeType, final String charset)
|
||||||
|
throws UnsupportedCharsetException {
|
||||||
|
return create(
|
||||||
|
mimeType,
|
||||||
|
(charset != null && charset.length() > 0) ? Charset
|
||||||
|
.forName(charset) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ContentType create(final String mimeType,
|
||||||
|
final Charset charset) {
|
||||||
if (mimeType == null) {
|
if (mimeType == null) {
|
||||||
throw new IllegalArgumentException("MIME type may not be null");
|
throw new IllegalArgumentException("MIME type may not be null");
|
||||||
}
|
}
|
||||||
@ -107,8 +155,84 @@ public final class ContentType implements Serializable {
|
|||||||
throw new IllegalArgumentException("MIME type may not be empty");
|
throw new IllegalArgumentException("MIME type may not be empty");
|
||||||
}
|
}
|
||||||
if (!valid(type)) {
|
if (!valid(type)) {
|
||||||
throw new IllegalArgumentException("MIME type may not contain reserved characters");
|
throw new IllegalArgumentException(
|
||||||
|
"MIME type may not contain reserved characters");
|
||||||
}
|
}
|
||||||
return new ContentType(MimeType.valueOf(type), charset);
|
return new ContentType(MimeType.valueOf(type), charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ContentType create(final MimeType mimeType,
|
||||||
|
final NameValue[] params, final boolean strict) {
|
||||||
|
Charset charset = null;
|
||||||
|
for (final NameValue param : params) {
|
||||||
|
if (param.getName().equalsIgnoreCase("charset")) {
|
||||||
|
final String s = param.getValue();
|
||||||
|
if (StringUtil.isNotBlank(s)) {
|
||||||
|
try {
|
||||||
|
charset = Charset.forName(s);
|
||||||
|
} catch (final UnsupportedCharsetException ex) {
|
||||||
|
if (strict) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ContentType(mimeType, charset, params != null
|
||||||
|
&& params.length > 0 ? params : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link ContentType} with the given parameters.
|
||||||
|
*
|
||||||
|
* @param mimeType MIME type. It may not be {@code null} or empty. It may not contain
|
||||||
|
* characters {@code <">, <;>, <,>} reserved by the HTTP specification.
|
||||||
|
* @param params parameters.
|
||||||
|
* @return content type
|
||||||
|
*
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public static ContentType create(final String mimeType,
|
||||||
|
final NameValue... params) throws UnsupportedCharsetException {
|
||||||
|
final String type = mimeType.toLowerCase(Locale.ROOT);
|
||||||
|
if (!valid(type)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"MIME type may not contain reserved characters");
|
||||||
|
}
|
||||||
|
return create(MimeType.valueOf(mimeType), params, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance with this MIME type and the given parameters.
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
* @return a new instance with this MIME type and the given parameters.
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public ContentType withParameters(final NameValue... params)
|
||||||
|
throws UnsupportedCharsetException {
|
||||||
|
if (params.length == 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
final Map<String, String> paramMap = new LinkedHashMap<String, String>();
|
||||||
|
if (this.params != null) {
|
||||||
|
for (final NameValue param : this.params) {
|
||||||
|
paramMap.put(param.getName(), param.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final NameValue param : params) {
|
||||||
|
paramMap.put(param.getName(), param.getValue());
|
||||||
|
}
|
||||||
|
final List<NameValue> newParams = new ArrayList<NameValue>(
|
||||||
|
paramMap.size() + 1);
|
||||||
|
if (this.charset != null && !paramMap.containsKey("charset")) {
|
||||||
|
newParams.add(new NameValue("charset", this.charset.name()));
|
||||||
|
}
|
||||||
|
for (final Map.Entry<String, String> entry : paramMap.entrySet()) {
|
||||||
|
newParams.add(new NameValue(entry.getKey(), entry.getValue()));
|
||||||
|
}
|
||||||
|
return create(this.getMimeType(),
|
||||||
|
newParams.toArray(new NameValue[newParams.size()]), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -25,8 +25,11 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import org.apache.http.Consts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants and static helpers related to the HTTP protocol.
|
* Constants and static helpers related to the HTTP protocol.
|
||||||
@ -63,11 +66,52 @@ public final class HTTP {
|
|||||||
public static final String CHUNK_CODING = "chunked";
|
public static final String CHUNK_CODING = "chunked";
|
||||||
public static final String IDENTITY_CODING = "identity";
|
public static final String IDENTITY_CODING = "identity";
|
||||||
|
|
||||||
public static boolean isWhitespace(char ch) {
|
public static final Charset DEF_CONTENT_CHARSET = Consts.ISO_8859_1;
|
||||||
|
public static final Charset DEF_PROTOCOL_CHARSET = Consts.ASCII;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final String UTF_8 = "UTF-8";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final String UTF_16 = "UTF-16";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final String US_ASCII = "US-ASCII";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final String ASCII = "ASCII";
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_CONTENT_CHARSET = UTF_8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_PROTOCOL_CHARSET = US_ASCII;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public final static String OCTET_STREAM_TYPE = "application/octet-stream";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public final static String PLAIN_TEXT_TYPE = "text/plain";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public final static String CHARSET_PARAM = "; charset=";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public final static String DEFAULT_CONTENT_TYPE = OCTET_STREAM_TYPE;
|
||||||
|
|
||||||
|
public static boolean isWhitespace(final char ch) {
|
||||||
return ch == SP || ch == HT || ch == CR || ch == LF;
|
return ch == SP || ch == HT || ch == CR || ch == LF;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HTTP() {
|
private HTTP() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.util.CharArrayBuffer;
|
||||||
|
import com.foxinmy.weixin4j.util.NameValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic implementation for formatting header value elements.
|
||||||
|
* Instances of this class are stateless and thread-safe.
|
||||||
|
* Derived classes are expected to maintain these properties.
|
||||||
|
*
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public class HeaderValueFormatter {
|
||||||
|
|
||||||
|
public final static HeaderValueFormatter INSTANCE = new HeaderValueFormatter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special characters that can be used as separators in HTTP parameters.
|
||||||
|
* These special characters MUST be in a quoted string to be used within
|
||||||
|
* a parameter value .
|
||||||
|
*/
|
||||||
|
public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsafe special characters that must be escaped using the backslash
|
||||||
|
* character
|
||||||
|
*/
|
||||||
|
public final static String UNSAFE_CHARS = "\"\\";
|
||||||
|
|
||||||
|
public HeaderValueFormatter() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a set of parameters.
|
||||||
|
*
|
||||||
|
* @param nvps the parameters to format
|
||||||
|
* @param quote <code>true</code> to always format with quoted values,
|
||||||
|
* <code>false</code> to use quotes only when necessary
|
||||||
|
* @param formatter the formatter to use, or <code>null</code>
|
||||||
|
* for the {@link #INSTANCE default}
|
||||||
|
*
|
||||||
|
* @return the formatted parameters
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
String formatParameters(final NameValue[] nvps,
|
||||||
|
final boolean quote,
|
||||||
|
final HeaderValueFormatter formatter) {
|
||||||
|
return (formatter != null ? formatter : HeaderValueFormatter.INSTANCE)
|
||||||
|
.formatParameters(null, nvps, quote).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// non-javadoc, see interface HeaderValueFormatter
|
||||||
|
public CharArrayBuffer formatParameters(final CharArrayBuffer charBuffer,
|
||||||
|
final NameValue[] nvps,
|
||||||
|
final boolean quote) {
|
||||||
|
final int len = estimateParametersLen(nvps);
|
||||||
|
CharArrayBuffer buffer = charBuffer;
|
||||||
|
if (buffer == null) {
|
||||||
|
buffer = new CharArrayBuffer(len);
|
||||||
|
} else {
|
||||||
|
buffer.ensureCapacity(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < nvps.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
buffer.append("; ");
|
||||||
|
}
|
||||||
|
formatNameValuePair(buffer, nvps[i], quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the length of formatted parameters.
|
||||||
|
*
|
||||||
|
* @param nvps the parameters to format, or <code>null</code>
|
||||||
|
*
|
||||||
|
* @return a length estimate, in number of characters
|
||||||
|
*/
|
||||||
|
protected int estimateParametersLen(final NameValue[] nvps) {
|
||||||
|
if ((nvps == null) || (nvps.length < 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = (nvps.length-1) * 2; // "; " between the parameters
|
||||||
|
for (final NameValue nvp : nvps) {
|
||||||
|
result += estimateNameValuePairLen(nvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a name-value pair.
|
||||||
|
*
|
||||||
|
* @param nvp the name-value pair to format
|
||||||
|
* @param quote <code>true</code> to always format with a quoted value,
|
||||||
|
* <code>false</code> to use quotes only when necessary
|
||||||
|
* @param formatter the formatter to use, or <code>null</code>
|
||||||
|
* for the {@link #INSTANCE default}
|
||||||
|
*
|
||||||
|
* @return the formatted name-value pair
|
||||||
|
*/
|
||||||
|
public static
|
||||||
|
String formatNameValuePair(final NameValue nvp,
|
||||||
|
final boolean quote,
|
||||||
|
final HeaderValueFormatter formatter) {
|
||||||
|
return (formatter != null ? formatter : HeaderValueFormatter.INSTANCE)
|
||||||
|
.formatNameValuePair(null, nvp, quote).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// non-javadoc, see interface HeaderValueFormatter
|
||||||
|
public CharArrayBuffer formatNameValuePair(final CharArrayBuffer charBuffer,
|
||||||
|
final NameValue nvp,
|
||||||
|
final boolean quote) {
|
||||||
|
final int len = estimateNameValuePairLen(nvp);
|
||||||
|
CharArrayBuffer buffer = charBuffer;
|
||||||
|
if (buffer == null) {
|
||||||
|
buffer = new CharArrayBuffer(len);
|
||||||
|
} else {
|
||||||
|
buffer.ensureCapacity(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append(nvp.getName());
|
||||||
|
final String value = nvp.getValue();
|
||||||
|
if (value != null) {
|
||||||
|
buffer.append('=');
|
||||||
|
doFormatValue(buffer, value, quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimates the length of a formatted name-value pair.
|
||||||
|
*
|
||||||
|
* @param nvp the name-value pair to format, or <code>null</code>
|
||||||
|
*
|
||||||
|
* @return a length estimate, in number of characters
|
||||||
|
*/
|
||||||
|
protected int estimateNameValuePairLen(final NameValue nvp) {
|
||||||
|
if (nvp == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = nvp.getName().length(); // name
|
||||||
|
final String value = nvp.getValue();
|
||||||
|
if (value != null) {
|
||||||
|
// assume quotes, but no escaped characters
|
||||||
|
result += 3 + value.length(); // ="value"
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually formats the value of a name-value pair.
|
||||||
|
* This does not include a leading = character.
|
||||||
|
* Called from {@link #formatNameValuePair formatNameValuePair}.
|
||||||
|
*
|
||||||
|
* @param buffer the buffer to append to, never <code>null</code>
|
||||||
|
* @param value the value to append, never <code>null</code>
|
||||||
|
* @param quote <code>true</code> to always format with quotes,
|
||||||
|
* <code>false</code> to use quotes only when necessary
|
||||||
|
*/
|
||||||
|
protected void doFormatValue(final CharArrayBuffer buffer,
|
||||||
|
final String value,
|
||||||
|
final boolean quote) {
|
||||||
|
|
||||||
|
boolean quoteFlag = quote;
|
||||||
|
if (!quoteFlag) {
|
||||||
|
for (int i = 0; (i < value.length()) && !quoteFlag; i++) {
|
||||||
|
quoteFlag = isSeparator(value.charAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quoteFlag) {
|
||||||
|
buffer.append('"');
|
||||||
|
}
|
||||||
|
for (int i = 0; i < value.length(); i++) {
|
||||||
|
final char ch = value.charAt(i);
|
||||||
|
if (isUnsafe(ch)) {
|
||||||
|
buffer.append('\\');
|
||||||
|
}
|
||||||
|
buffer.append(ch);
|
||||||
|
}
|
||||||
|
if (quoteFlag) {
|
||||||
|
buffer.append('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a character is a {@link #SEPARATORS separator}.
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the character is a separator,
|
||||||
|
* <code>false</code> otherwise
|
||||||
|
*/
|
||||||
|
protected boolean isSeparator(final char ch) {
|
||||||
|
return SEPARATORS.indexOf(ch) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the character is unsafe,
|
||||||
|
* <code>false</code> otherwise
|
||||||
|
*/
|
||||||
|
protected boolean isUnsafe(final char ch) {
|
||||||
|
return UNSAFE_CHARS.indexOf(ch) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // class BasicHeaderValueFormatter
|
||||||
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many
|
|
||||||
* individuals on behalf of the Apache Software Foundation. For more
|
|
||||||
* information on the Apache Software Foundation, please see
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents common content properties.
|
|
||||||
*/
|
|
||||||
public interface ContentDescriptor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the body descriptors MIME type.
|
|
||||||
* @see #getMediaType()
|
|
||||||
* @see #getSubType()
|
|
||||||
* @return The MIME type, which has been parsed from the
|
|
||||||
* content-type definition. Must not be null, but
|
|
||||||
* "text/plain", if no content-type was specified.
|
|
||||||
*/
|
|
||||||
String getMimeType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the defaulted MIME media type for this content.
|
|
||||||
* For example <code>TEXT</code>, <code>IMAGE</code>, <code>MULTIPART</code>
|
|
||||||
* @see #getMimeType()
|
|
||||||
* @return the MIME media type when content-type specified,
|
|
||||||
* otherwise the correct default (<code>TEXT</code>)
|
|
||||||
*/
|
|
||||||
String getMediaType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the defaulted MIME sub type for this content.
|
|
||||||
* @see #getMimeType()
|
|
||||||
* @return the MIME media type when content-type is specified,
|
|
||||||
* otherwise the correct default (<code>PLAIN</code>)
|
|
||||||
*/
|
|
||||||
String getSubType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>The body descriptors character set, defaulted appropriately for the MIME type.</p>
|
|
||||||
* <p>
|
|
||||||
* For <code>TEXT</code> types, this will be defaulted to <code>us-ascii</code>.
|
|
||||||
* For other types, when the charset parameter is missing this property will be null.
|
|
||||||
* </p>
|
|
||||||
* @return Character set, which has been parsed from the
|
|
||||||
* content-type definition. Not null for <code>TEXT</code> types, when unset will
|
|
||||||
* be set to default <code>us-ascii</code>. For other types, when unset,
|
|
||||||
* null will be returned.
|
|
||||||
*/
|
|
||||||
String getCharset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the body descriptors transfer encoding.
|
|
||||||
* @return The transfer encoding. Must not be null, but "7bit",
|
|
||||||
* if no transfer-encoding was specified.
|
|
||||||
*/
|
|
||||||
String getTransferEncoding();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the body descriptors content-length.
|
|
||||||
* @return Content length, if known, or -1, to indicate the absence of a
|
|
||||||
* content-length header.
|
|
||||||
*/
|
|
||||||
long getContentLength();
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many
|
|
||||||
* individuals on behalf of the Apache Software Foundation. For more
|
|
||||||
* information on the Apache Software Foundation, please see
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.util.NameValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The header of an entity (see RFC 2045).
|
|
||||||
*/
|
|
||||||
public class Header implements Iterable<NameValue> {
|
|
||||||
|
|
||||||
private final List<NameValue> fields;
|
|
||||||
private final Map<String, List<NameValue>> fieldMap;
|
|
||||||
|
|
||||||
public Header() {
|
|
||||||
super();
|
|
||||||
this.fields = new LinkedList<NameValue>();
|
|
||||||
this.fieldMap = new HashMap<String, List<NameValue>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addField(final NameValue field) {
|
|
||||||
if (field == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String key = field.getName().toLowerCase(Locale.US);
|
|
||||||
List<NameValue> values = this.fieldMap.get(key);
|
|
||||||
if (values == null) {
|
|
||||||
values = new LinkedList<NameValue>();
|
|
||||||
this.fieldMap.put(key, values);
|
|
||||||
}
|
|
||||||
values.add(field);
|
|
||||||
this.fields.add(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<NameValue> getFields() {
|
|
||||||
return new ArrayList<NameValue>(this.fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NameValue getField(final String name) {
|
|
||||||
if (name == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String key = name.toLowerCase(Locale.US);
|
|
||||||
List<NameValue> list = this.fieldMap.get(key);
|
|
||||||
if (list != null && !list.isEmpty()) {
|
|
||||||
return list.get(0);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<NameValue> getFields(final String name) {
|
|
||||||
if (name == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String key = name.toLowerCase(Locale.US);
|
|
||||||
List<NameValue> list = this.fieldMap.get(key);
|
|
||||||
if (list == null || list.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
} else {
|
|
||||||
return new ArrayList<NameValue>(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int removeFields(final String name) {
|
|
||||||
if (name == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
String key = name.toLowerCase(Locale.US);
|
|
||||||
List<NameValue> removed = fieldMap.remove(key);
|
|
||||||
if (removed == null || removed.isEmpty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
this.fields.removeAll(removed);
|
|
||||||
return removed.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setField(final NameValue field) {
|
|
||||||
if (field == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String key = field.getName().toLowerCase(Locale.US);
|
|
||||||
List<NameValue> list = fieldMap.get(key);
|
|
||||||
if (list == null || list.isEmpty()) {
|
|
||||||
addField(field);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
list.clear();
|
|
||||||
list.add(field);
|
|
||||||
int firstOccurrence = -1;
|
|
||||||
int index = 0;
|
|
||||||
for (Iterator<NameValue> it = this.fields.iterator(); it.hasNext(); index++) {
|
|
||||||
NameValue f = it.next();
|
|
||||||
if (f.getName().equalsIgnoreCase(field.getName())) {
|
|
||||||
it.remove();
|
|
||||||
if (firstOccurrence == -1) {
|
|
||||||
firstOccurrence = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.fields.add(firstOccurrence, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator<NameValue> iterator() {
|
|
||||||
return Collections.unmodifiableList(fields).iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.fields.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many
|
|
||||||
* individuals on behalf of the Apache Software Foundation. For more
|
|
||||||
* information on the Apache Software Foundation, please see
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.CharBuffer;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.util.NameValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HttpMultipart represents a collection of MIME multipart encoded content
|
|
||||||
* bodies. This class is capable of operating either in the strict (RFC 822, RFC
|
|
||||||
* 2045, RFC 2046 compliant) or the browser compatible modes.
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public class HttpMultipart {
|
|
||||||
|
|
||||||
private static ByteArrayBuffer encode(final Charset charset,
|
|
||||||
final String string) {
|
|
||||||
ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
|
|
||||||
ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
|
|
||||||
bab.append(encoded.array(), encoded.position(), encoded.remaining());
|
|
||||||
return bab;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeBytes(final ByteArrayBuffer b,
|
|
||||||
final OutputStream out) throws IOException {
|
|
||||||
out.write(b.buffer(), 0, b.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeBytes(final String s, final Charset charset,
|
|
||||||
final OutputStream out) throws IOException {
|
|
||||||
ByteArrayBuffer b = encode(charset, s);
|
|
||||||
writeBytes(b, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeBytes(final String s, final OutputStream out)
|
|
||||||
throws IOException {
|
|
||||||
ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
|
|
||||||
writeBytes(b, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeField(final NameValue field, final OutputStream out)
|
|
||||||
throws IOException {
|
|
||||||
writeBytes(field.getName(), out);
|
|
||||||
writeBytes(FIELD_SEP, out);
|
|
||||||
writeBytes(field.getValue(), out);
|
|
||||||
writeBytes(CR_LF, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeField(final NameValue field, final Charset charset,
|
|
||||||
final OutputStream out) throws IOException {
|
|
||||||
writeBytes(field.getName(), charset, out);
|
|
||||||
writeBytes(FIELD_SEP, out);
|
|
||||||
writeBytes(field.getValue(), charset, out);
|
|
||||||
writeBytes(CR_LF, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final ByteArrayBuffer FIELD_SEP = encode(
|
|
||||||
MIME.DEFAULT_CHARSET, ": ");
|
|
||||||
private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET,
|
|
||||||
"\r\n");
|
|
||||||
private static final ByteArrayBuffer TWO_DASHES = encode(
|
|
||||||
MIME.DEFAULT_CHARSET, "--");
|
|
||||||
|
|
||||||
private final String subType;
|
|
||||||
private final Charset charset;
|
|
||||||
private final String boundary;
|
|
||||||
private final List<FormBodyPart> parts;
|
|
||||||
|
|
||||||
private final HttpMultipartMode mode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance with the specified settings.
|
|
||||||
*
|
|
||||||
* @param subType
|
|
||||||
* mime subtype - must not be {@code null}
|
|
||||||
* @param charset
|
|
||||||
* the character set to use. May be {@code null}, in which case
|
|
||||||
* {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
|
||||||
* @param boundary
|
|
||||||
* to use - must not be {@code null}
|
|
||||||
* @param mode
|
|
||||||
* the mode to use
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if charset is null or boundary is null
|
|
||||||
*/
|
|
||||||
public HttpMultipart(final String subType, final Charset charset,
|
|
||||||
final String boundary, HttpMultipartMode mode) {
|
|
||||||
super();
|
|
||||||
if (subType == null) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Multipart subtype may not be null");
|
|
||||||
}
|
|
||||||
if (boundary == null) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Multipart boundary may not be null");
|
|
||||||
}
|
|
||||||
this.subType = subType;
|
|
||||||
this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
|
|
||||||
this.boundary = boundary;
|
|
||||||
this.parts = new ArrayList<FormBodyPart>();
|
|
||||||
this.mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance with the specified settings. Mode is set to
|
|
||||||
* {@link HttpMultipartMode#STRICT}
|
|
||||||
*
|
|
||||||
* @param subType
|
|
||||||
* mime subtype - must not be {@code null}
|
|
||||||
* @param charset
|
|
||||||
* the character set to use. May be {@code null}, in which case
|
|
||||||
* {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
|
||||||
* @param boundary
|
|
||||||
* to use - must not be {@code null}
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if charset is null or boundary is null
|
|
||||||
*/
|
|
||||||
public HttpMultipart(final String subType, final Charset charset,
|
|
||||||
final String boundary) {
|
|
||||||
this(subType, charset, boundary, HttpMultipartMode.STRICT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpMultipart(final String subType, final String boundary) {
|
|
||||||
this(subType, null, boundary);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSubType() {
|
|
||||||
return this.subType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Charset getCharset() {
|
|
||||||
return this.charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpMultipartMode getMode() {
|
|
||||||
return this.mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FormBodyPart> getBodyParts() {
|
|
||||||
return this.parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addBodyPart(final FormBodyPart part) {
|
|
||||||
if (part == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.parts.add(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBoundary() {
|
|
||||||
return this.boundary;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doWriteTo(final HttpMultipartMode mode,
|
|
||||||
final OutputStream out, boolean writeContent) throws IOException {
|
|
||||||
|
|
||||||
ByteArrayBuffer boundary = encode(this.charset, getBoundary());
|
|
||||||
for (FormBodyPart part : this.parts) {
|
|
||||||
writeBytes(TWO_DASHES, out);
|
|
||||||
writeBytes(boundary, out);
|
|
||||||
writeBytes(CR_LF, out);
|
|
||||||
|
|
||||||
Header header = part.getHeader();
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case STRICT:
|
|
||||||
for (NameValue field : header) {
|
|
||||||
writeField(field, out);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BROWSER_COMPATIBLE:
|
|
||||||
// Only write Content-Disposition
|
|
||||||
// Use content charset
|
|
||||||
NameValue cd = part.getHeader().getField(
|
|
||||||
MIME.CONTENT_DISPOSITION);
|
|
||||||
writeField(cd, this.charset, out);
|
|
||||||
String filename = part.getBody().getFilename();
|
|
||||||
if (filename != null) {
|
|
||||||
NameValue ct = part.getHeader().getField(MIME.CONTENT_TYPE);
|
|
||||||
writeField(ct, this.charset, out);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
writeBytes(CR_LF, out);
|
|
||||||
|
|
||||||
if (writeContent) {
|
|
||||||
part.getBody().writeTo(out);
|
|
||||||
}
|
|
||||||
writeBytes(CR_LF, out);
|
|
||||||
}
|
|
||||||
writeBytes(TWO_DASHES, out);
|
|
||||||
writeBytes(boundary, out);
|
|
||||||
writeBytes(TWO_DASHES, out);
|
|
||||||
writeBytes(CR_LF, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out the content in the multipart/form encoding. This method
|
|
||||||
* produces slightly different formatting depending on its compatibility
|
|
||||||
* mode.
|
|
||||||
*
|
|
||||||
* @see #getMode()
|
|
||||||
*/
|
|
||||||
public void writeTo(final OutputStream out) throws IOException {
|
|
||||||
doWriteTo(this.mode, out, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the total length of the multipart content (content length of
|
|
||||||
* individual parts plus that of extra elements required to delimit the
|
|
||||||
* parts from one another). If any of the @{link BodyPart}s contained in
|
|
||||||
* this object is of a streaming entity of unknown length the total length
|
|
||||||
* is also unknown.
|
|
||||||
* <p/>
|
|
||||||
* This method buffers only a small amount of data in order to determine the
|
|
||||||
* total length of the entire entity. The content of individual parts is not
|
|
||||||
* buffered.
|
|
||||||
*
|
|
||||||
* @return total length of the multipart entity if known, <code>-1</code>
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
public long getTotalLength() {
|
|
||||||
long contentLen = 0;
|
|
||||||
for (FormBodyPart part : this.parts) {
|
|
||||||
ContentBody body = part.getBody();
|
|
||||||
long len = body.getContentLength();
|
|
||||||
if (len >= 0) {
|
|
||||||
contentLen += len;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
try {
|
|
||||||
doWriteTo(this.mode, out, false);
|
|
||||||
byte[] extra = out.toByteArray();
|
|
||||||
return contentLen + extra.length;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// Should never happen
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package com.foxinmy.weixin4j.http.apache;
|
|
||||||
|
|
||||||
public enum HttpMultipartMode {
|
|
||||||
|
|
||||||
/** RFC 822, RFC 2045, RFC 2046 compliant */
|
|
||||||
STRICT,
|
|
||||||
/**
|
|
||||||
* browser-compatible mode, i.e. only write Content-Disposition; use content
|
|
||||||
* charset
|
|
||||||
*/
|
|
||||||
BROWSER_COMPATIBLE
|
|
||||||
}
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many
|
|
||||||
* individuals on behalf of the Apache Software Foundation. For more
|
|
||||||
* information on the Apache Software Foundation, please see
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public class InputStreamBody extends AbstractContentBody {
|
|
||||||
|
|
||||||
private final InputStream in;
|
|
||||||
private final String filename;
|
|
||||||
private int contentLength;
|
|
||||||
|
|
||||||
public InputStreamBody(final InputStream in, final String mimeType,
|
|
||||||
final String filename) {
|
|
||||||
super(mimeType);
|
|
||||||
if (in == null) {
|
|
||||||
throw new IllegalArgumentException("Input stream may not be null");
|
|
||||||
}
|
|
||||||
this.in = in;
|
|
||||||
this.filename = filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputStreamBody(final InputStream in, final String filename) {
|
|
||||||
this(in, "application/octet-stream", filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
|
||||||
return this.in;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo(final OutputStream out) throws IOException {
|
|
||||||
if (out == null) {
|
|
||||||
throw new IllegalArgumentException("Output stream may not be null");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
byte[] tmp = new byte[4096];
|
|
||||||
int l;
|
|
||||||
while ((l = this.in.read(tmp)) != -1) {
|
|
||||||
out.write(tmp, 0, l);
|
|
||||||
contentLength += l;
|
|
||||||
}
|
|
||||||
out.flush();
|
|
||||||
} finally {
|
|
||||||
this.in.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTransferEncoding() {
|
|
||||||
return MIME.ENC_BINARY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCharset() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getContentLength() {
|
|
||||||
try {
|
|
||||||
return Math.max(contentLength, this.in.available());
|
|
||||||
} catch (IOException e) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFilename() {
|
|
||||||
return this.filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many
|
|
||||||
* individuals on behalf of the Apache Software Foundation. For more
|
|
||||||
* information on the Apache Software Foundation, please see
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.http.ContentType;
|
|
||||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multipart/form coded HTTP entity consisting of multiple body parts.
|
|
||||||
*
|
|
||||||
* @since 4.0
|
|
||||||
*/
|
|
||||||
public class MultipartEntity implements HttpEntity {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The pool of ASCII chars to be used for generating a multipart boundary.
|
|
||||||
*/
|
|
||||||
private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
.toCharArray();
|
|
||||||
|
|
||||||
private final HttpMultipart multipart;
|
|
||||||
|
|
||||||
// @GuardedBy("dirty") // we always read dirty before accessing length
|
|
||||||
private long length;
|
|
||||||
private volatile boolean dirty; // used to decide whether to recalculate
|
|
||||||
// length
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance using the specified parameters
|
|
||||||
*
|
|
||||||
* @param mode
|
|
||||||
* the mode to use, may be {@code null}, in which case
|
|
||||||
* {@link HttpMultipartMode#STRICT} is used
|
|
||||||
* @param boundary
|
|
||||||
* the boundary string, may be {@code null}, in which case
|
|
||||||
* {@link #generateBoundary()} is invoked to create the string
|
|
||||||
* @param charset
|
|
||||||
* the character set to use, may be {@code null}, in which case
|
|
||||||
* {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
|
||||||
*/
|
|
||||||
public MultipartEntity(HttpMultipartMode mode, String boundary,
|
|
||||||
Charset charset) {
|
|
||||||
super();
|
|
||||||
if (boundary == null) {
|
|
||||||
boundary = generateBoundary();
|
|
||||||
}
|
|
||||||
if (mode == null) {
|
|
||||||
mode = HttpMultipartMode.STRICT;
|
|
||||||
}
|
|
||||||
this.multipart = new HttpMultipart("form-data", charset, boundary, mode);
|
|
||||||
this.dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance using the specified {@link HttpMultipartMode} mode.
|
|
||||||
* Boundary and charset are set to {@code null}.
|
|
||||||
*
|
|
||||||
* @param mode
|
|
||||||
* the desired mode
|
|
||||||
*/
|
|
||||||
public MultipartEntity(final HttpMultipartMode mode) {
|
|
||||||
this(mode, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance using mode {@link HttpMultipartMode#STRICT}
|
|
||||||
*/
|
|
||||||
public MultipartEntity() {
|
|
||||||
this(HttpMultipartMode.STRICT, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String generateBoundary() {
|
|
||||||
StringBuilder buffer = new StringBuilder();
|
|
||||||
Random rand = new Random();
|
|
||||||
int count = rand.nextInt(11) + 30; // a random size from 30 to 40
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
|
|
||||||
}
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPart(final FormBodyPart bodyPart) {
|
|
||||||
this.multipart.addBodyPart(bodyPart);
|
|
||||||
this.dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPart(final String name, final ContentBody contentBody) {
|
|
||||||
addPart(new FormBodyPart(name, contentBody));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRepeatable() {
|
|
||||||
for (FormBodyPart part : this.multipart.getBodyParts()) {
|
|
||||||
ContentBody body = part.getBody();
|
|
||||||
if (body.getContentLength() < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isChunked() {
|
|
||||||
return !isRepeatable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isStreaming() {
|
|
||||||
return !isRepeatable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getContentLength() {
|
|
||||||
if (this.dirty) {
|
|
||||||
this.length = this.multipart.getTotalLength();
|
|
||||||
this.dirty = false;
|
|
||||||
}
|
|
||||||
return this.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContentType getContentType() {
|
|
||||||
return ContentType.MULTIPART_FORM_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void consumeContent() throws IOException,
|
|
||||||
UnsupportedOperationException {
|
|
||||||
if (isStreaming()) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"Streaming entity does not implement #consumeContent()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getContent() throws IOException,
|
|
||||||
UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"Multipart form entity does not implement #getContent()");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(final OutputStream outstream) throws IOException {
|
|
||||||
this.multipart.writeTo(outstream);
|
|
||||||
outstream.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -25,7 +25,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http.apache.content;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.MimeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -33,36 +38,31 @@ package com.foxinmy.weixin4j.http.apache;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractContentBody implements ContentBody {
|
public abstract class AbstractContentBody implements ContentBody {
|
||||||
|
|
||||||
private final String mimeType;
|
private final ContentType contentType;
|
||||||
private final String mediaType;
|
|
||||||
private final String subType;
|
|
||||||
|
|
||||||
public AbstractContentBody(final String mimeType) {
|
/**
|
||||||
super();
|
* @since 4.3
|
||||||
if (mimeType == null) {
|
*/
|
||||||
throw new IllegalArgumentException("MIME type may not be null");
|
public AbstractContentBody(final ContentType contentType) {
|
||||||
}
|
super();
|
||||||
this.mimeType = mimeType;
|
this.contentType = contentType;
|
||||||
int i = mimeType.indexOf('/');
|
}
|
||||||
if (i != -1) {
|
|
||||||
this.mediaType = mimeType.substring(0, i);
|
|
||||||
this.subType = mimeType.substring(i + 1);
|
|
||||||
} else {
|
|
||||||
this.mediaType = mimeType;
|
|
||||||
this.subType = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMimeType() {
|
/**
|
||||||
return this.mimeType;
|
* @since 4.3
|
||||||
}
|
*/
|
||||||
|
public ContentType getContentType() {
|
||||||
|
return this.contentType;
|
||||||
|
}
|
||||||
|
|
||||||
public String getMediaType() {
|
@Override
|
||||||
return this.mediaType;
|
public MimeType getMimeType() {
|
||||||
}
|
return this.contentType.getMimeType();
|
||||||
|
}
|
||||||
public String getSubType() {
|
|
||||||
return this.subType;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCharset() {
|
||||||
|
final Charset charset = this.contentType.getCharset();
|
||||||
|
return charset != null ? charset.name() : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -24,13 +24,18 @@
|
|||||||
* <http://www.apache.org/>.
|
* <http://www.apache.org/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http.apache.content;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Body part that is built using a byte array containing a file.
|
* Binary body part backed by a byte array.
|
||||||
|
*
|
||||||
|
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||||
*
|
*
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
@ -50,14 +55,19 @@ public class ByteArrayBody extends AbstractContentBody {
|
|||||||
* Creates a new ByteArrayBody.
|
* Creates a new ByteArrayBody.
|
||||||
*
|
*
|
||||||
* @param data The contents of the file contained in this part.
|
* @param data The contents of the file contained in this part.
|
||||||
* @param mimeType The mime type of the file contained in this part.
|
* @param mimeType The MIME type of the file contained in this part.
|
||||||
* @param filename The name of the file contained in this part.
|
* @param filename The name of the file contained in this part.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public ByteArrayBody(final byte[] data, final String mimeType, final String filename) {
|
public ByteArrayBody(final byte[] data, final String mimeType, final String filename) {
|
||||||
super(mimeType);
|
this(data, ContentType.create(mimeType), filename);
|
||||||
if (data == null) {
|
}
|
||||||
throw new IllegalArgumentException("byte[] may not be null");
|
|
||||||
}
|
/**
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public ByteArrayBody(final byte[] data, final ContentType contentType, final String filename) {
|
||||||
|
super(contentType);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
}
|
}
|
||||||
@ -72,22 +82,27 @@ public class ByteArrayBody extends AbstractContentBody {
|
|||||||
this(data, "application/octet-stream", filename);
|
this(data, "application/octet-stream", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void writeTo(final OutputStream out) throws IOException {
|
public void writeTo(final OutputStream out) throws IOException {
|
||||||
out.write(data);
|
out.write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getCharset() {
|
public String getCharset() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getTransferEncoding() {
|
public String getTransferEncoding() {
|
||||||
return MIME.ENC_BINARY;
|
return MIME.ENC_BINARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public long getContentLength() {
|
public long getContentLength() {
|
||||||
return data.length;
|
return data.length;
|
||||||
}
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.content;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.MimeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public interface ContentBody {
|
||||||
|
/**
|
||||||
|
* Returns the body descriptors MIME type.
|
||||||
|
*
|
||||||
|
* @return The MIME type, which has been parsed from the content-type
|
||||||
|
* definition. Must not be null, but "text/plain", if no
|
||||||
|
* content-type was specified.
|
||||||
|
*/
|
||||||
|
MimeType getMimeType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The body descriptors character set, defaulted appropriately for the MIME
|
||||||
|
* type.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* For {@code TEXT} types, this will be defaulted to {@code us-ascii}. For
|
||||||
|
* other types, when the charset parameter is missing this property will be
|
||||||
|
* null.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Character set, which has been parsed from the content-type
|
||||||
|
* definition. Not null for {@code TEXT} types, when unset will be
|
||||||
|
* set to default {@code us-ascii}. For other types, when unset,
|
||||||
|
* null will be returned.
|
||||||
|
*/
|
||||||
|
String getCharset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the body descriptors transfer encoding.
|
||||||
|
*
|
||||||
|
* @return The transfer encoding. Must not be null, but "7bit", if no
|
||||||
|
* transfer-encoding was specified.
|
||||||
|
*/
|
||||||
|
String getTransferEncoding();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the body descriptors content-length.
|
||||||
|
*
|
||||||
|
* @return Content length, if known, or -1, to indicate the absence of a
|
||||||
|
* content-length header.
|
||||||
|
*/
|
||||||
|
long getContentLength();
|
||||||
|
|
||||||
|
String getFilename();
|
||||||
|
|
||||||
|
void writeTo(OutputStream out) throws IOException;
|
||||||
|
}
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.content;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary body part backed by a file.
|
||||||
|
*
|
||||||
|
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||||
|
*
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public class FileBody extends AbstractContentBody {
|
||||||
|
|
||||||
|
private final File file;
|
||||||
|
private final String filename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.1
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public FileBody(final File file, final String filename,
|
||||||
|
final String mimeType, final String charset) {
|
||||||
|
this(file, ContentType.create(mimeType, charset), filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.1
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public FileBody(final File file, final String mimeType, final String charset) {
|
||||||
|
this(file, null, mimeType, charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public FileBody(final File file, final String mimeType) {
|
||||||
|
this(file, ContentType.create(mimeType), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileBody(final File file) {
|
||||||
|
this(file, ContentType.DEFAULT_BINARY, file != null ? file.getName()
|
||||||
|
: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public FileBody(final File file, final ContentType contentType,
|
||||||
|
final String filename) {
|
||||||
|
super(contentType);
|
||||||
|
this.file = file;
|
||||||
|
this.filename = filename == null ? file.getName() : filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public FileBody(final File file, final ContentType contentType) {
|
||||||
|
this(file, contentType, file != null ? file.getName() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return new FileInputStream(this.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(final OutputStream out) throws IOException {
|
||||||
|
final InputStream in = new FileInputStream(this.file);
|
||||||
|
try {
|
||||||
|
final byte[] tmp = new byte[4096];
|
||||||
|
int l;
|
||||||
|
while ((l = in.read(tmp)) != -1) {
|
||||||
|
out.write(tmp, 0, l);
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTransferEncoding() {
|
||||||
|
return MIME.ENC_BINARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLength() {
|
||||||
|
return this.file.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return this.file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -25,99 +25,86 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http.apache.content;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Binary body part backed by an input stream.
|
||||||
|
*
|
||||||
|
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
public class FileBody extends AbstractContentBody {
|
public class InputStreamBody extends AbstractContentBody {
|
||||||
|
|
||||||
private final File file;
|
private final InputStream in;
|
||||||
private final String filename;
|
private final String filename;
|
||||||
private final String charset;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public FileBody(final File file,
|
public InputStreamBody(final InputStream in, final String mimeType, final String filename) {
|
||||||
final String filename,
|
this(in, ContentType.create(mimeType), filename);
|
||||||
final String mimeType,
|
}
|
||||||
final String charset) {
|
|
||||||
super(mimeType);
|
public InputStreamBody(final InputStream in, final String filename) {
|
||||||
if (file == null) {
|
this(in, ContentType.DEFAULT_BINARY, filename);
|
||||||
throw new IllegalArgumentException("File may not be null");
|
|
||||||
}
|
|
||||||
this.file = file;
|
|
||||||
if (filename != null)
|
|
||||||
this.filename = filename;
|
|
||||||
else
|
|
||||||
this.filename = file.getName();
|
|
||||||
this.charset = charset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.1
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public FileBody(final File file,
|
public InputStreamBody(final InputStream in, final ContentType contentType, final String filename) {
|
||||||
final String mimeType,
|
super(contentType);
|
||||||
final String charset) {
|
this.in = in;
|
||||||
this(file, null, mimeType, charset);
|
this.filename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileBody(final File file, final String mimeType) {
|
/**
|
||||||
this(file, mimeType, null);
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public InputStreamBody(final InputStream in, final ContentType contentType) {
|
||||||
|
this(in, contentType, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileBody(final File file) {
|
public InputStream getInputStream() {
|
||||||
this(file, "application/octet-stream");
|
return this.in;
|
||||||
}
|
|
||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
|
||||||
return new FileInputStream(this.file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void writeTo(final OutputStream out) throws IOException {
|
public void writeTo(final OutputStream out) throws IOException {
|
||||||
if (out == null) {
|
|
||||||
throw new IllegalArgumentException("Output stream may not be null");
|
|
||||||
}
|
|
||||||
InputStream in = new FileInputStream(this.file);
|
|
||||||
try {
|
try {
|
||||||
byte[] tmp = new byte[4096];
|
final byte[] tmp = new byte[4096];
|
||||||
int l;
|
int l;
|
||||||
while ((l = in.read(tmp)) != -1) {
|
while ((l = this.in.read(tmp)) != -1) {
|
||||||
out.write(tmp, 0, l);
|
out.write(tmp, 0, l);
|
||||||
}
|
}
|
||||||
out.flush();
|
out.flush();
|
||||||
} finally {
|
} finally {
|
||||||
in.close();
|
this.in.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getTransferEncoding() {
|
public String getTransferEncoding() {
|
||||||
return MIME.ENC_BINARY;
|
return MIME.ENC_BINARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCharset() {
|
@Override
|
||||||
return charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getContentLength() {
|
public long getContentLength() {
|
||||||
return this.file.length();
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return filename;
|
return this.filename;
|
||||||
}
|
|
||||||
|
|
||||||
public File getFile() {
|
|
||||||
return this.file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http.apache.content;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -36,32 +36,46 @@ import java.io.Reader;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.mime.MIME;
|
||||||
|
import com.foxinmy.weixin4j.util.Consts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Text body part backed by a byte array.
|
||||||
|
*
|
||||||
|
* @see org.apache.http.entity.mime.MultipartEntityBuilder
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
public class StringBody extends AbstractContentBody {
|
public class StringBody extends AbstractContentBody {
|
||||||
|
|
||||||
private final byte[] content;
|
private final byte[] content;
|
||||||
private final Charset charset;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
*
|
||||||
|
* @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)}
|
||||||
|
* or {@link org.apache.http.entity.mime.MultipartEntityBuilder}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static StringBody create(
|
public static StringBody create(
|
||||||
final String text,
|
final String text,
|
||||||
final String mimeType,
|
final String mimeType,
|
||||||
final Charset charset) throws IllegalArgumentException {
|
final Charset charset) throws IllegalArgumentException {
|
||||||
try {
|
try {
|
||||||
return new StringBody(text, mimeType, charset);
|
return new StringBody(text, mimeType, charset);
|
||||||
} catch (UnsupportedEncodingException ex) {
|
} catch (final UnsupportedEncodingException ex) {
|
||||||
throw new IllegalArgumentException("Charset " + charset + " is not supported", ex);
|
throw new IllegalArgumentException("Charset " + charset + " is not supported", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
*
|
||||||
|
* @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)}
|
||||||
|
* or {@link org.apache.http.entity.mime.MultipartEntityBuilder}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static StringBody create(
|
public static StringBody create(
|
||||||
final String text, final Charset charset) throws IllegalArgumentException {
|
final String text, final Charset charset) throws IllegalArgumentException {
|
||||||
return create(text, null, charset);
|
return create(text, null, charset);
|
||||||
@ -69,43 +83,41 @@ public class StringBody extends AbstractContentBody {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
*
|
||||||
|
* @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)}
|
||||||
|
* or {@link org.apache.http.entity.mime.MultipartEntityBuilder}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static StringBody create(final String text) throws IllegalArgumentException {
|
public static StringBody create(final String text) throws IllegalArgumentException {
|
||||||
return create(text, null, null);
|
return create(text, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a StringBody from the specified text, mime type and character set.
|
* Create a StringBody from the specified text, MIME type and character set.
|
||||||
*
|
*
|
||||||
* @param text to be used for the body, not {@code null}
|
* @param text to be used for the body, not {@code null}
|
||||||
* @param mimeType the mime type, not {@code null}
|
* @param mimeType the MIME type, not {@code null}
|
||||||
* @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
|
* @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
|
||||||
* @throws UnsupportedEncodingException
|
* @throws UnsupportedEncodingException
|
||||||
* @throws IllegalArgumentException if the {@code text} parameter is null
|
* @throws IllegalArgumentException if the {@code text} parameter is null
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public StringBody(
|
public StringBody(
|
||||||
final String text,
|
final String text,
|
||||||
final String mimeType,
|
final String mimeType,
|
||||||
Charset charset) throws UnsupportedEncodingException {
|
final Charset charset) throws UnsupportedEncodingException {
|
||||||
super(mimeType);
|
this(text, ContentType.create(mimeType, charset != null ? charset : Consts.UTF_8));
|
||||||
if (text == null) {
|
|
||||||
throw new IllegalArgumentException("Text may not be null");
|
|
||||||
}
|
|
||||||
if (charset == null) {
|
|
||||||
charset = Charset.forName("US-ASCII");
|
|
||||||
}
|
|
||||||
this.content = text.getBytes(charset.name());
|
|
||||||
this.charset = charset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a StringBody from the specified text and character set.
|
* Create a StringBody from the specified text and character set.
|
||||||
* The mime type is set to "text/plain".
|
* The MIME type is set to "text/plain".
|
||||||
*
|
*
|
||||||
* @param text to be used for the body, not {@code null}
|
* @param text to be used for the body, not {@code null}
|
||||||
* @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
|
* @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
|
||||||
* @throws UnsupportedEncodingException
|
* @throws UnsupportedEncodingException
|
||||||
* @throws IllegalArgumentException if the {@code text} parameter is null
|
* @throws IllegalArgumentException if the {@code text} parameter is null
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public StringBody(final String text, final Charset charset) throws UnsupportedEncodingException {
|
public StringBody(final String text, final Charset charset) throws UnsupportedEncodingException {
|
||||||
this(text, "text/plain", charset);
|
this(text, "text/plain", charset);
|
||||||
@ -113,29 +125,38 @@ public class StringBody extends AbstractContentBody {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a StringBody from the specified text.
|
* Create a StringBody from the specified text.
|
||||||
* The mime type is set to "text/plain".
|
* The MIME type is set to "text/plain".
|
||||||
* The hosts default charset is used.
|
* The {@linkplain Consts#ASCII ASCII} charset is used.
|
||||||
*
|
*
|
||||||
* @param text to be used for the body, not {@code null}
|
* @param text to be used for the body, not {@code null}
|
||||||
* @throws UnsupportedEncodingException
|
* @throws UnsupportedEncodingException
|
||||||
* @throws IllegalArgumentException if the {@code text} parameter is null
|
* @throws IllegalArgumentException if the {@code text} parameter is null
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public StringBody(final String text) throws UnsupportedEncodingException {
|
public StringBody(final String text) throws UnsupportedEncodingException {
|
||||||
this(text, "text/plain", null);
|
this(text, "text/plain", Consts.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public StringBody(final String text, final ContentType contentType) {
|
||||||
|
super(contentType);
|
||||||
|
final Charset charset = contentType.getCharset();
|
||||||
|
this.content = text.getBytes(charset != null ? charset : Consts.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reader getReader() {
|
public Reader getReader() {
|
||||||
|
final Charset charset = getContentType().getCharset();
|
||||||
return new InputStreamReader(
|
return new InputStreamReader(
|
||||||
new ByteArrayInputStream(this.content),
|
new ByteArrayInputStream(this.content),
|
||||||
this.charset);
|
charset != null ? charset : Consts.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void writeTo(final OutputStream out) throws IOException {
|
public void writeTo(final OutputStream out) throws IOException {
|
||||||
if (out == null) {
|
final InputStream in = new ByteArrayInputStream(this.content);
|
||||||
throw new IllegalArgumentException("Output stream may not be null");
|
final byte[] tmp = new byte[4096];
|
||||||
}
|
|
||||||
InputStream in = new ByteArrayInputStream(this.content);
|
|
||||||
byte[] tmp = new byte[4096];
|
|
||||||
int l;
|
int l;
|
||||||
while ((l = in.read(tmp)) != -1) {
|
while ((l = in.read(tmp)) != -1) {
|
||||||
out.write(tmp, 0, l);
|
out.write(tmp, 0, l);
|
||||||
@ -143,20 +164,18 @@ public class StringBody extends AbstractContentBody {
|
|||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getTransferEncoding() {
|
public String getTransferEncoding() {
|
||||||
return MIME.ENC_8BIT;
|
return MIME.ENC_8BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCharset() {
|
@Override
|
||||||
return this.charset.name();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getContentLength() {
|
public long getContentLength() {
|
||||||
return this.content.length;
|
return this.content.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||||
|
import com.foxinmy.weixin4j.util.ByteArrayBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is
|
||||||
|
* capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or
|
||||||
|
* the browser compatible modes.
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
abstract class AbstractMultipartForm {
|
||||||
|
|
||||||
|
private static ByteArrayBuffer encode(
|
||||||
|
final Charset charset, final String string) {
|
||||||
|
final ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
|
||||||
|
final ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
|
||||||
|
bab.append(encoded.array(), encoded.position(), encoded.remaining());
|
||||||
|
return bab;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeBytes(
|
||||||
|
final ByteArrayBuffer b, final OutputStream out) throws IOException {
|
||||||
|
out.write(b.buffer(), 0, b.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeBytes(
|
||||||
|
final String s, final Charset charset, final OutputStream out) throws IOException {
|
||||||
|
final ByteArrayBuffer b = encode(charset, s);
|
||||||
|
writeBytes(b, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeBytes(
|
||||||
|
final String s, final OutputStream out) throws IOException {
|
||||||
|
final ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
|
||||||
|
writeBytes(b, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void writeField(
|
||||||
|
final MinimalField field, final OutputStream out) throws IOException {
|
||||||
|
writeBytes(field.getName(), out);
|
||||||
|
writeBytes(FIELD_SEP, out);
|
||||||
|
writeBytes(field.getBody(), out);
|
||||||
|
writeBytes(CR_LF, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void writeField(
|
||||||
|
final MinimalField field, final Charset charset, final OutputStream out) throws IOException {
|
||||||
|
writeBytes(field.getName(), charset, out);
|
||||||
|
writeBytes(FIELD_SEP, out);
|
||||||
|
writeBytes(field.getBody(), charset, out);
|
||||||
|
writeBytes(CR_LF, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": ");
|
||||||
|
private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
|
||||||
|
private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
|
||||||
|
|
||||||
|
final Charset charset;
|
||||||
|
final String boundary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance with the specified settings.
|
||||||
|
*
|
||||||
|
* @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||||
|
* @param boundary to use - must not be {@code null}
|
||||||
|
* @throws IllegalArgumentException if charset is null or boundary is null
|
||||||
|
*/
|
||||||
|
public AbstractMultipartForm(final Charset charset, final String boundary) {
|
||||||
|
super();
|
||||||
|
this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
|
||||||
|
this.boundary = boundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractMultipartForm(final String boundary) {
|
||||||
|
this(null, boundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract List<FormBodyPart> getBodyParts();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
formatMultipartHeader(part, out);
|
||||||
|
|
||||||
|
writeBytes(CR_LF, out);
|
||||||
|
|
||||||
|
if (writeContent) {
|
||||||
|
part.getBody().writeTo(out);
|
||||||
|
}
|
||||||
|
writeBytes(CR_LF, out);
|
||||||
|
}
|
||||||
|
writeBytes(TWO_DASHES, out);
|
||||||
|
writeBytes(boundaryEncoded, out);
|
||||||
|
writeBytes(TWO_DASHES, out);
|
||||||
|
writeBytes(CR_LF, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the multipart header fields; depends on the style.
|
||||||
|
*/
|
||||||
|
protected abstract void formatMultipartHeader(
|
||||||
|
final FormBodyPart part,
|
||||||
|
final OutputStream out) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes out the content in the multipart/form encoding. This method
|
||||||
|
* produces slightly different formatting depending on its compatibility
|
||||||
|
* mode.
|
||||||
|
*/
|
||||||
|
public void writeTo(final OutputStream out) throws IOException {
|
||||||
|
doWriteTo(out, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the total length of the multipart content (content length of
|
||||||
|
* individual parts plus that of extra elements required to delimit the parts
|
||||||
|
* from one another). If any of the @{link BodyPart}s contained in this object
|
||||||
|
* is of a streaming entity of unknown length the total length is also unknown.
|
||||||
|
* <p>
|
||||||
|
* This method buffers only a small amount of data in order to determine the
|
||||||
|
* total length of the entire entity. The content of individual parts is not
|
||||||
|
* buffered.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return total length of the multipart entity if known, {@code -1}
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
public long getTotalLength() {
|
||||||
|
long contentLen = 0;
|
||||||
|
for (final FormBodyPart part: getBodyParts()) {
|
||||||
|
final ContentBody body = part.getBody();
|
||||||
|
final long len = body.getContentLength();
|
||||||
|
if (len >= 0) {
|
||||||
|
contentLen += len;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
doWriteTo(out, false);
|
||||||
|
final byte[] extra = out.toByteArray();
|
||||||
|
return contentLen + extra.length;
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
// Should never happen
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -25,10 +25,11 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.util.NameValue;
|
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.AbstractContentBody;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FormBodyPart class represents a content body that can be used as a part of multipart encoded
|
* FormBodyPart class represents a content body that can be used as a part of multipart encoded
|
||||||
@ -41,17 +42,20 @@ public class FormBodyPart {
|
|||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Header header;
|
private final Header header;
|
||||||
|
|
||||||
private final ContentBody body;
|
private final ContentBody body;
|
||||||
|
|
||||||
|
FormBodyPart(final String name, final ContentBody body, final Header header) {
|
||||||
|
super();
|
||||||
|
this.name = name;
|
||||||
|
this.body = body;
|
||||||
|
this.header = header != null ? header : new Header();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||||
|
*/
|
||||||
public FormBodyPart(final String name, final ContentBody body) {
|
public FormBodyPart(final String name, final ContentBody body) {
|
||||||
super();
|
super();
|
||||||
if (name == null) {
|
|
||||||
throw new IllegalArgumentException("Name may not be null");
|
|
||||||
}
|
|
||||||
if (body == null) {
|
|
||||||
throw new IllegalArgumentException("Body may not be null");
|
|
||||||
}
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.header = new Header();
|
this.header = new Header();
|
||||||
@ -74,14 +78,14 @@ public class FormBodyPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addField(final String name, final String value) {
|
public void addField(final String name, final String value) {
|
||||||
if (name == null) {
|
this.header.addField(new MinimalField(name, value));
|
||||||
throw new IllegalArgumentException("Field name may not be null");
|
|
||||||
}
|
|
||||||
this.header.addField(new NameValue(name, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||||
|
*/
|
||||||
protected void generateContentDisp(final ContentBody body) {
|
protected void generateContentDisp(final ContentBody body) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
final StringBuilder buffer = new StringBuilder();
|
||||||
buffer.append("form-data; name=\"");
|
buffer.append("form-data; name=\"");
|
||||||
buffer.append(getName());
|
buffer.append(getName());
|
||||||
buffer.append("\"");
|
buffer.append("\"");
|
||||||
@ -93,18 +97,33 @@ public class FormBodyPart {
|
|||||||
addField(MIME.CONTENT_DISPOSITION, buffer.toString());
|
addField(MIME.CONTENT_DISPOSITION, buffer.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||||
|
*/
|
||||||
protected void generateContentType(final ContentBody body) {
|
protected void generateContentType(final ContentBody body) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
final ContentType contentType;
|
||||||
buffer.append(body.getMimeType()); // MimeType cannot be null
|
if (body instanceof AbstractContentBody) {
|
||||||
if (body.getCharset() != null) { // charset may legitimately be null
|
contentType = ((AbstractContentBody) body).getContentType();
|
||||||
buffer.append("; charset=");
|
} else {
|
||||||
buffer.append(body.getCharset());
|
contentType = null;
|
||||||
|
}
|
||||||
|
if (contentType != null) {
|
||||||
|
addField(MIME.CONTENT_TYPE, contentType.toString());
|
||||||
|
} else {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append(body.getMimeType()); // MimeType cannot be null
|
||||||
|
if (body.getCharset() != null) { // charset may legitimately be null
|
||||||
|
buffer.append("; charset=");
|
||||||
|
buffer.append(body.getCharset());
|
||||||
|
}
|
||||||
|
addField(MIME.CONTENT_TYPE, buffer.toString());
|
||||||
}
|
}
|
||||||
addField(MIME.CONTENT_TYPE, buffer.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (4.4) use {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPartBuilder}.
|
||||||
|
*/
|
||||||
protected void generateTransferEncoding(final ContentBody body) {
|
protected void generateTransferEncoding(final ContentBody body) {
|
||||||
addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null
|
addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.AbstractContentBody;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for individual {@link com.foxinmy.weixin4j.http.apache.mime.FormBodyPart}s.
|
||||||
|
*
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public class FormBodyPartBuilder {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private ContentBody body;
|
||||||
|
private final Header header;
|
||||||
|
|
||||||
|
public static FormBodyPartBuilder create(final String name, final ContentBody body) {
|
||||||
|
return new FormBodyPartBuilder(name, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormBodyPartBuilder create() {
|
||||||
|
return new FormBodyPartBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
FormBodyPartBuilder(final String name, final ContentBody body) {
|
||||||
|
this();
|
||||||
|
this.name = name;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormBodyPartBuilder() {
|
||||||
|
this.header = new Header();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBodyPartBuilder setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBodyPartBuilder setBody(final ContentBody body) {
|
||||||
|
this.body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBodyPartBuilder addField(final String name, final String value) {
|
||||||
|
this.header.addField(new MinimalField(name, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBodyPartBuilder setField(final String name, final String value) {
|
||||||
|
this.header.setField(new MinimalField(name, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBodyPartBuilder removeFields(final String name) {
|
||||||
|
this.header.removeFields(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBodyPart build() {
|
||||||
|
final Header headerCopy = new Header();
|
||||||
|
final List<MinimalField> fields = this.header.getFields();
|
||||||
|
for (final MinimalField field: fields) {
|
||||||
|
headerCopy.addField(field);
|
||||||
|
}
|
||||||
|
if (headerCopy.getField(MIME.CONTENT_DISPOSITION) == null) {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append("form-data; name=\"");
|
||||||
|
buffer.append(encodeForHeader(this.name));
|
||||||
|
buffer.append("\"");
|
||||||
|
if (this.body.getFilename() != null) {
|
||||||
|
buffer.append("; filename=\"");
|
||||||
|
buffer.append(encodeForHeader(this.body.getFilename()));
|
||||||
|
buffer.append("\"");
|
||||||
|
}
|
||||||
|
headerCopy.addField(new MinimalField(MIME.CONTENT_DISPOSITION, buffer.toString()));
|
||||||
|
}
|
||||||
|
if (headerCopy.getField(MIME.CONTENT_TYPE) == null) {
|
||||||
|
final ContentType contentType;
|
||||||
|
if (body instanceof AbstractContentBody) {
|
||||||
|
contentType = ((AbstractContentBody) body).getContentType();
|
||||||
|
} else {
|
||||||
|
contentType = null;
|
||||||
|
}
|
||||||
|
if (contentType != null) {
|
||||||
|
headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, contentType.toString()));
|
||||||
|
} else {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append(this.body.getMimeType()); // MimeType cannot be null
|
||||||
|
if (this.body.getCharset() != null) { // charset may legitimately be null
|
||||||
|
buffer.append("; charset=");
|
||||||
|
buffer.append(this.body.getCharset());
|
||||||
|
}
|
||||||
|
headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, buffer.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (headerCopy.getField(MIME.CONTENT_TRANSFER_ENC) == null) {
|
||||||
|
// TE cannot be null
|
||||||
|
headerCopy.addField(new MinimalField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()));
|
||||||
|
}
|
||||||
|
return new FormBodyPart(this.name, this.body, headerCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String encodeForHeader(final String headerName) {
|
||||||
|
if (headerName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < headerName.length(); i++) {
|
||||||
|
final char x = headerName.charAt(i);
|
||||||
|
if (x == '"' || x == '\\' || x == '\r') {
|
||||||
|
sb.append("\\");
|
||||||
|
}
|
||||||
|
sb.append(x);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The header of an entity (see RFC 2045).
|
||||||
|
*/
|
||||||
|
public class Header implements Iterable<MinimalField> {
|
||||||
|
|
||||||
|
private final List<MinimalField> fields;
|
||||||
|
private final Map<String, List<MinimalField>> fieldMap;
|
||||||
|
|
||||||
|
public Header() {
|
||||||
|
super();
|
||||||
|
this.fields = new LinkedList<MinimalField>();
|
||||||
|
this.fieldMap = new HashMap<String, List<MinimalField>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addField(final MinimalField field) {
|
||||||
|
if (field == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String key = field.getName().toLowerCase(Locale.ROOT);
|
||||||
|
List<MinimalField> values = this.fieldMap.get(key);
|
||||||
|
if (values == null) {
|
||||||
|
values = new LinkedList<MinimalField>();
|
||||||
|
this.fieldMap.put(key, values);
|
||||||
|
}
|
||||||
|
values.add(field);
|
||||||
|
this.fields.add(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MinimalField> getFields() {
|
||||||
|
return new ArrayList<MinimalField>(this.fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MinimalField getField(final String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String key = name.toLowerCase(Locale.ROOT);
|
||||||
|
final List<MinimalField> list = this.fieldMap.get(key);
|
||||||
|
if (list != null && !list.isEmpty()) {
|
||||||
|
return list.get(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MinimalField> getFields(final String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String key = name.toLowerCase(Locale.ROOT);
|
||||||
|
final List<MinimalField> list = this.fieldMap.get(key);
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
return new ArrayList<MinimalField>(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int removeFields(final String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final String key = name.toLowerCase(Locale.ROOT);
|
||||||
|
final List<MinimalField> removed = fieldMap.remove(key);
|
||||||
|
if (removed == null || removed.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
this.fields.removeAll(removed);
|
||||||
|
return removed.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setField(final MinimalField field) {
|
||||||
|
if (field == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String key = field.getName().toLowerCase(Locale.ROOT);
|
||||||
|
final List<MinimalField> list = fieldMap.get(key);
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
addField(field);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.clear();
|
||||||
|
list.add(field);
|
||||||
|
int firstOccurrence = -1;
|
||||||
|
int index = 0;
|
||||||
|
for (final Iterator<MinimalField> it = this.fields.iterator(); it.hasNext(); index++) {
|
||||||
|
final MinimalField f = it.next();
|
||||||
|
if (f.getName().equalsIgnoreCase(field.getName())) {
|
||||||
|
it.remove();
|
||||||
|
if (firstOccurrence == -1) {
|
||||||
|
firstOccurrence = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.fields.add(firstOccurrence, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<MinimalField> iterator() {
|
||||||
|
return Collections.unmodifiableList(fields).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.fields.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpBrowserCompatibleMultipart represents a collection of MIME multipart encoded
|
||||||
|
* content bodies. This class is emulates browser compatibility, e.g. IE 5 or earlier.
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
class HttpBrowserCompatibleMultipart extends AbstractMultipartForm {
|
||||||
|
|
||||||
|
private final List<FormBodyPart> parts;
|
||||||
|
|
||||||
|
public HttpBrowserCompatibleMultipart(
|
||||||
|
final Charset charset,
|
||||||
|
final String boundary,
|
||||||
|
final List<FormBodyPart> parts) {
|
||||||
|
super(charset, boundary);
|
||||||
|
this.parts = parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FormBodyPart> getBodyParts() {
|
||||||
|
return this.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the multipart header fields; depends on the style.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void formatMultipartHeader(
|
||||||
|
final FormBodyPart part,
|
||||||
|
final OutputStream out) throws IOException {
|
||||||
|
// For browser-compatible, only write Content-Disposition
|
||||||
|
// Use content charset
|
||||||
|
final Header header = part.getHeader();
|
||||||
|
final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION);
|
||||||
|
writeField(cd, this.charset, out);
|
||||||
|
final String filename = part.getBody().getFilename();
|
||||||
|
if (filename != null) {
|
||||||
|
final MinimalField ct = header.getField(MIME.CONTENT_TYPE);
|
||||||
|
writeField(ct, this.charset, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is
|
||||||
|
* capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or
|
||||||
|
* the browser compatible modes.
|
||||||
|
*
|
||||||
|
* @since 4.0
|
||||||
|
*
|
||||||
|
* @deprecated (4.3) Use {@link MultipartEntityBuilder}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class HttpMultipart extends AbstractMultipartForm {
|
||||||
|
|
||||||
|
private final HttpMultipartMode mode;
|
||||||
|
private final List<FormBodyPart> parts;
|
||||||
|
|
||||||
|
private final String subType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance with the specified settings.
|
||||||
|
*
|
||||||
|
* @param subType MIME subtype - must not be {@code null}
|
||||||
|
* @param charset the character set to use. May be {@code null},
|
||||||
|
* in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||||
|
* @param boundary to use - must not be {@code null}
|
||||||
|
* @param mode the mode to use
|
||||||
|
* @throws IllegalArgumentException if charset is null or boundary is null
|
||||||
|
*/
|
||||||
|
public HttpMultipart(
|
||||||
|
final String subType, final Charset charset, final String boundary,
|
||||||
|
final HttpMultipartMode mode) {
|
||||||
|
super(charset, boundary);
|
||||||
|
this.subType = subType;
|
||||||
|
this.mode = mode;
|
||||||
|
this.parts = new ArrayList<FormBodyPart>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance with the specified settings.
|
||||||
|
* Mode is set to {@link HttpMultipartMode#STRICT}
|
||||||
|
*
|
||||||
|
* @param subType MIME subtype - must not be {@code null}
|
||||||
|
* @param charset the character set to use. May be {@code null},
|
||||||
|
* in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||||
|
* @param boundary to use - must not be {@code null}
|
||||||
|
* @throws IllegalArgumentException if charset is null or boundary is null
|
||||||
|
*/
|
||||||
|
public HttpMultipart(final String subType, final Charset charset, final String boundary) {
|
||||||
|
this(subType, charset, boundary, HttpMultipartMode.STRICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpMultipart(final String subType, final String boundary) {
|
||||||
|
this(subType, null, boundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpMultipartMode getMode() {
|
||||||
|
return this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void formatMultipartHeader(
|
||||||
|
final FormBodyPart part, final OutputStream out) throws IOException {
|
||||||
|
final Header header = part.getHeader();
|
||||||
|
switch (this.mode) {
|
||||||
|
case BROWSER_COMPATIBLE:
|
||||||
|
// For browser-compatible, only write Content-Disposition
|
||||||
|
// Use content charset
|
||||||
|
final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION);
|
||||||
|
writeField(cd, this.charset, out);
|
||||||
|
final String filename = part.getBody().getFilename();
|
||||||
|
if (filename != null) {
|
||||||
|
final MinimalField ct = header.getField(MIME.CONTENT_TYPE);
|
||||||
|
writeField(ct, this.charset, out);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (final MinimalField field: header) {
|
||||||
|
writeField(field, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FormBodyPart> getBodyParts() {
|
||||||
|
return this.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBodyPart(final FormBodyPart part) {
|
||||||
|
if (part == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.parts.add(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubType() {
|
||||||
|
return this.subType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Charset getCharset() {
|
||||||
|
return this.charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBoundary() {
|
||||||
|
return this.boundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -25,19 +25,19 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
public interface ContentBody extends ContentDescriptor {
|
public enum HttpMultipartMode {
|
||||||
|
|
||||||
String getFilename();
|
/** RFC 822, RFC 2045, RFC 2046 compliant */
|
||||||
|
STRICT,
|
||||||
void writeTo(OutputStream out) throws IOException;
|
/** browser-compatible mode, i.e. only write Content-Disposition; use content charset */
|
||||||
|
BROWSER_COMPATIBLE,
|
||||||
|
/** RFC 6532 compliant */
|
||||||
|
RFC6532
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpRFC6532Multipart represents a collection of MIME multipart encoded content bodies,
|
||||||
|
* implementing the strict (RFC 822, RFC 2045, RFC 2046 compliant) interpretation
|
||||||
|
* of the spec, with the exception of allowing UTF-8 headers, as per RFC6532.
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
class HttpRFC6532Multipart extends AbstractMultipartForm {
|
||||||
|
|
||||||
|
private final List<FormBodyPart> parts;
|
||||||
|
|
||||||
|
public HttpRFC6532Multipart(
|
||||||
|
final Charset charset,
|
||||||
|
final String boundary,
|
||||||
|
final List<FormBodyPart> parts) {
|
||||||
|
super(charset, boundary);
|
||||||
|
this.parts = parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FormBodyPart> getBodyParts() {
|
||||||
|
return this.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void formatMultipartHeader(
|
||||||
|
final FormBodyPart part,
|
||||||
|
final OutputStream out) throws IOException {
|
||||||
|
|
||||||
|
// For RFC6532, we output all fields with UTF-8 encoding.
|
||||||
|
final Header header = part.getHeader();
|
||||||
|
for (final MinimalField field: header) {
|
||||||
|
writeField(field, MIME.UTF8_CHARSET, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpStrictMultipart represents a collection of MIME multipart encoded content bodies,
|
||||||
|
* implementing the strict (RFC 822, RFC 2045, RFC 2046 compliant) interpretation
|
||||||
|
* of the spec.
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
class HttpStrictMultipart extends AbstractMultipartForm {
|
||||||
|
|
||||||
|
private final List<FormBodyPart> parts;
|
||||||
|
|
||||||
|
public HttpStrictMultipart(
|
||||||
|
final Charset charset,
|
||||||
|
final String boundary,
|
||||||
|
final List<FormBodyPart> parts) {
|
||||||
|
super(charset, boundary);
|
||||||
|
this.parts = parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FormBodyPart> getBodyParts() {
|
||||||
|
return this.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void formatMultipartHeader(
|
||||||
|
final FormBodyPart part,
|
||||||
|
final OutputStream out) throws IOException {
|
||||||
|
|
||||||
|
// For strict, we output all fields with MIME-standard encoding.
|
||||||
|
final Header header = part.getHeader();
|
||||||
|
for (final MinimalField field: header) {
|
||||||
|
writeField(field, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -25,10 +25,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.util.Consts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
@ -42,7 +44,8 @@ public final class MIME {
|
|||||||
public static final String ENC_8BIT = "8bit";
|
public static final String ENC_8BIT = "8bit";
|
||||||
public static final String ENC_BINARY = "binary";
|
public static final String ENC_BINARY = "binary";
|
||||||
|
|
||||||
/** The default character set to be used, i.e. "US-ASCII" */
|
public static final Charset DEFAULT_CHARSET = Consts.UTF_8;
|
||||||
public static final Charset DEFAULT_CHARSET = Charset.forName("US-ASCII");
|
|
||||||
|
public static final Charset UTF8_CHARSET = Consts.UTF_8;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal MIME field.
|
||||||
|
*
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public class MinimalField {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public MinimalField(final String name, final String value) {
|
||||||
|
super();
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append(this.name);
|
||||||
|
buffer.append(": ");
|
||||||
|
buffer.append(this.value);
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||||
|
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multipart/form coded HTTP entity consisting of multiple body parts.
|
||||||
|
*
|
||||||
|
* @since 4.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MultipartEntity implements HttpEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pool of ASCII chars to be used for generating a multipart boundary.
|
||||||
|
*/
|
||||||
|
private final static char[] MULTIPART_CHARS =
|
||||||
|
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
.toCharArray();
|
||||||
|
|
||||||
|
private final MultipartEntityBuilder builder;
|
||||||
|
private volatile MultipartFormEntity entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance using the specified parameters
|
||||||
|
* @param mode the mode to use, may be {@code null}, in which case {@link HttpMultipartMode#STRICT} is used
|
||||||
|
* @param boundary the boundary string, may be {@code null}, in which case {@link #generateBoundary()} is invoked to create the string
|
||||||
|
* @param charset the character set to use, may be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
|
||||||
|
*/
|
||||||
|
public MultipartEntity(
|
||||||
|
final HttpMultipartMode mode,
|
||||||
|
final String boundary,
|
||||||
|
final Charset charset) {
|
||||||
|
super();
|
||||||
|
this.builder = new MultipartEntityBuilder()
|
||||||
|
.setMode(mode)
|
||||||
|
.setCharset(charset != null ? charset : MIME.DEFAULT_CHARSET)
|
||||||
|
.setBoundary(boundary);
|
||||||
|
this.entity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance using the specified {@link HttpMultipartMode} mode.
|
||||||
|
* Boundary and charset are set to {@code null}.
|
||||||
|
* @param mode the desired mode
|
||||||
|
*/
|
||||||
|
public MultipartEntity(final HttpMultipartMode mode) {
|
||||||
|
this(mode, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance using mode {@link HttpMultipartMode#STRICT}
|
||||||
|
*/
|
||||||
|
public MultipartEntity() {
|
||||||
|
this(HttpMultipartMode.STRICT, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String generateContentType(
|
||||||
|
final String boundary,
|
||||||
|
final Charset charset) {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append("multipart/form-data; boundary=");
|
||||||
|
buffer.append(boundary);
|
||||||
|
if (charset != null) {
|
||||||
|
buffer.append("; charset=");
|
||||||
|
buffer.append(charset.name());
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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 40
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MultipartFormEntity getEntity() {
|
||||||
|
if (this.entity == null) {
|
||||||
|
this.entity = this.builder.buildEntity();
|
||||||
|
}
|
||||||
|
return this.entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPart(final FormBodyPart bodyPart) {
|
||||||
|
this.builder.addPart(bodyPart);
|
||||||
|
this.entity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPart(final String name, final ContentBody contentBody) {
|
||||||
|
addPart(new FormBodyPart(name, contentBody));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRepeatable() {
|
||||||
|
return getEntity().isRepeatable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChunked() {
|
||||||
|
return getEntity().isChunked();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStreaming() {
|
||||||
|
return getEntity().isStreaming();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLength() {
|
||||||
|
return getEntity().getContentLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentType getContentType() {
|
||||||
|
return getEntity().getContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getContent() throws IOException, UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Multipart form entity does not implement #getContent()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(final OutputStream outstream) throws IOException {
|
||||||
|
getEntity().writeTo(outstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.ByteArrayBody;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.ContentBody;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.FileBody;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||||
|
import com.foxinmy.weixin4j.http.apache.content.StringBody;
|
||||||
|
import com.foxinmy.weixin4j.util.NameValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for multipart {@link HttpEntity}s.
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public class MultipartEntityBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pool of ASCII chars to be used for generating a multipart boundary.
|
||||||
|
*/
|
||||||
|
private final static char[] MULTIPART_CHARS =
|
||||||
|
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
.toCharArray();
|
||||||
|
|
||||||
|
private final static String DEFAULT_SUBTYPE = "form-data";
|
||||||
|
|
||||||
|
private ContentType contentType;
|
||||||
|
private HttpMultipartMode mode = HttpMultipartMode.STRICT;
|
||||||
|
private String boundary = null;
|
||||||
|
private Charset charset = null;
|
||||||
|
private List<FormBodyPart> bodyParts = null;
|
||||||
|
|
||||||
|
public static MultipartEntityBuilder create() {
|
||||||
|
return new MultipartEntityBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
MultipartEntityBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder setMode(final HttpMultipartMode mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder setLaxMode() {
|
||||||
|
this.mode = HttpMultipartMode.BROWSER_COMPATIBLE;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder setStrictMode() {
|
||||||
|
this.mode = HttpMultipartMode.STRICT;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder setBoundary(final String boundary) {
|
||||||
|
this.boundary = boundary;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public MultipartEntityBuilder setMimeSubtype(final String subType) {
|
||||||
|
this.contentType = ContentType.create("multipart/" + subType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.4
|
||||||
|
*
|
||||||
|
* @deprecated (4.5) Use {@link #setContentType(org.apache.http.entity.ContentType)}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public MultipartEntityBuilder seContentType(final ContentType contentType) {
|
||||||
|
return setContentType(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.5
|
||||||
|
*/
|
||||||
|
public MultipartEntityBuilder setContentType(final ContentType contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder setCharset(final Charset charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public MultipartEntityBuilder addPart(final FormBodyPart bodyPart) {
|
||||||
|
if (bodyPart == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (this.bodyParts == null) {
|
||||||
|
this.bodyParts = new ArrayList<FormBodyPart>();
|
||||||
|
}
|
||||||
|
this.bodyParts.add(bodyPart);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) {
|
||||||
|
return addPart(FormBodyPartBuilder.create(name, contentBody).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addTextBody(
|
||||||
|
final String name, final String text, final ContentType contentType) {
|
||||||
|
return addPart(name, new StringBody(text, contentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addTextBody(
|
||||||
|
final String name, final String text) {
|
||||||
|
return addTextBody(name, text, ContentType.DEFAULT_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addBinaryBody(
|
||||||
|
final String name, final byte[] b, final ContentType contentType, final String filename) {
|
||||||
|
return addPart(name, new ByteArrayBody(b, contentType, filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addBinaryBody(
|
||||||
|
final String name, final byte[] b) {
|
||||||
|
return addBinaryBody(name, b, ContentType.DEFAULT_BINARY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addBinaryBody(
|
||||||
|
final String name, final File file, final ContentType contentType, final String filename) {
|
||||||
|
return addPart(name, new FileBody(file, contentType, filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addBinaryBody(
|
||||||
|
final String name, final File file) {
|
||||||
|
return addBinaryBody(name, file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addBinaryBody(
|
||||||
|
final String name, final InputStream stream, final ContentType contentType,
|
||||||
|
final String filename) {
|
||||||
|
return addPart(name, new InputStreamBody(stream, contentType, filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartEntityBuilder addBinaryBody(final String name, final InputStream stream) {
|
||||||
|
return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 40
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartFormEntity buildEntity() {
|
||||||
|
String boundaryCopy = boundary;
|
||||||
|
if (boundaryCopy == null && contentType != null) {
|
||||||
|
boundaryCopy = contentType.getParameter("boundary");
|
||||||
|
}
|
||||||
|
if (boundaryCopy == null) {
|
||||||
|
boundaryCopy = generateBoundary();
|
||||||
|
}
|
||||||
|
Charset charsetCopy = charset;
|
||||||
|
if (charsetCopy == null && contentType != null) {
|
||||||
|
charsetCopy = contentType.getCharset();
|
||||||
|
}
|
||||||
|
final List<NameValue> paramsList = new ArrayList<NameValue>(2);
|
||||||
|
paramsList.add(new NameValue("boundary", boundaryCopy));
|
||||||
|
if (charsetCopy != null) {
|
||||||
|
paramsList.add(new NameValue("charset", charsetCopy.name()));
|
||||||
|
}
|
||||||
|
final NameValue[] params = paramsList.toArray(new NameValue[paramsList.size()]);
|
||||||
|
final ContentType contentTypeCopy = contentType != null ?
|
||||||
|
contentType.withParameters(params) :
|
||||||
|
ContentType.create("multipart/" + DEFAULT_SUBTYPE, params);
|
||||||
|
final List<FormBodyPart> bodyPartsCopy = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) :
|
||||||
|
Collections.<FormBodyPart>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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.foxinmy.weixin4j.http.apache.mime;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.ContentType;
|
||||||
|
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||||
|
|
||||||
|
class MultipartFormEntity implements HttpEntity {
|
||||||
|
|
||||||
|
private final AbstractMultipartForm multipart;
|
||||||
|
private final ContentType contentType;
|
||||||
|
private final long contentLength;
|
||||||
|
|
||||||
|
MultipartFormEntity(
|
||||||
|
final AbstractMultipartForm multipart,
|
||||||
|
final ContentType contentType,
|
||||||
|
final long contentLength) {
|
||||||
|
super();
|
||||||
|
this.multipart = multipart;
|
||||||
|
this.contentType = contentType;
|
||||||
|
this.contentLength = contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractMultipartForm getMultipart() {
|
||||||
|
return this.multipart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRepeatable() {
|
||||||
|
return this.contentLength != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChunked() {
|
||||||
|
return !isRepeatable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStreaming() {
|
||||||
|
return !isRepeatable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLength() {
|
||||||
|
return this.contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentType getContentType() {
|
||||||
|
return this.contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getContent() throws IOException {
|
||||||
|
if (this.contentLength < 0) {
|
||||||
|
throw new IllegalArgumentException("Content length is unknown");
|
||||||
|
} else if (this.contentLength > 25 * 1024) {
|
||||||
|
throw new IllegalArgumentException("Content length is too long: " + this.contentLength);
|
||||||
|
}
|
||||||
|
final ByteArrayOutputStream outstream = new ByteArrayOutputStream();
|
||||||
|
writeTo(outstream);
|
||||||
|
outstream.flush();
|
||||||
|
return new ByteArrayInputStream(outstream.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(final OutputStream outstream) throws IOException {
|
||||||
|
this.multipart.writeTo(outstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,7 +33,7 @@ import com.foxinmy.weixin4j.http.HttpHeaders;
|
|||||||
import com.foxinmy.weixin4j.http.HttpMethod;
|
import com.foxinmy.weixin4j.http.HttpMethod;
|
||||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||||
import com.foxinmy.weixin4j.http.apache.MultipartEntity;
|
import com.foxinmy.weixin4j.http.apache.mime.MultipartEntity;
|
||||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||||
import com.foxinmy.weixin4j.util.Consts;
|
import com.foxinmy.weixin4j.util.Consts;
|
||||||
import com.foxinmy.weixin4j.util.StringUtil;
|
import com.foxinmy.weixin4j.util.StringUtil;
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import com.foxinmy.weixin4j.http.HttpClientException;
|
|||||||
import com.foxinmy.weixin4j.http.HttpHeaders;
|
import com.foxinmy.weixin4j.http.HttpHeaders;
|
||||||
import com.foxinmy.weixin4j.http.HttpMethod;
|
import com.foxinmy.weixin4j.http.HttpMethod;
|
||||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||||
import com.foxinmy.weixin4j.http.apache.MultipartEntity;
|
import com.foxinmy.weixin4j.http.apache.mime.MultipartEntity;
|
||||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||||
import com.foxinmy.weixin4j.util.StringUtil;
|
import com.foxinmy.weixin4j.util.StringUtil;
|
||||||
|
|
||||||
|
|||||||
@ -17,9 +17,9 @@ import com.foxinmy.weixin4j.http.HttpRequest;
|
|||||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||||
import com.foxinmy.weixin4j.http.MimeType;
|
import com.foxinmy.weixin4j.http.MimeType;
|
||||||
import com.foxinmy.weixin4j.http.URLParameter;
|
import com.foxinmy.weixin4j.http.URLParameter;
|
||||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||||
import com.foxinmy.weixin4j.http.apache.HttpMultipartMode;
|
import com.foxinmy.weixin4j.http.apache.mime.HttpMultipartMode;
|
||||||
import com.foxinmy.weixin4j.http.apache.MultipartEntity;
|
import com.foxinmy.weixin4j.http.apache.mime.MultipartEntityBuilder;
|
||||||
import com.foxinmy.weixin4j.http.entity.FormUrlEntity;
|
import com.foxinmy.weixin4j.http.entity.FormUrlEntity;
|
||||||
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
import com.foxinmy.weixin4j.http.entity.HttpEntity;
|
||||||
import com.foxinmy.weixin4j.http.entity.StringEntity;
|
import com.foxinmy.weixin4j.http.entity.StringEntity;
|
||||||
@ -85,13 +85,13 @@ public class WeixinRequestExecutor {
|
|||||||
*/
|
*/
|
||||||
public WeixinResponse post(String url, FormBodyPart... bodyParts)
|
public WeixinResponse post(String url, FormBodyPart... bodyParts)
|
||||||
throws WeixinException {
|
throws WeixinException {
|
||||||
MultipartEntity entity = new MultipartEntity(
|
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||||
HttpMultipartMode.BROWSER_COMPATIBLE, null, Consts.UTF_8);
|
|
||||||
for (FormBodyPart bodyPart : bodyParts) {
|
for (FormBodyPart bodyPart : bodyParts) {
|
||||||
entity.addPart(bodyPart);
|
builder.addPart(bodyPart);
|
||||||
}
|
}
|
||||||
HttpRequest request = new HttpRequest(HttpMethod.POST, url);
|
HttpRequest request = new HttpRequest(HttpMethod.POST, url);
|
||||||
request.setEntity(entity);
|
request.setEntity(builder.setMode(HttpMultipartMode.RFC6532)
|
||||||
|
.buildEntity());
|
||||||
return doRequest(request);
|
return doRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,11 +25,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.util;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resizable byte array.
|
* A resizable byte array.
|
||||||
*
|
*
|
||||||
@ -48,33 +47,30 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
*
|
*
|
||||||
* @param capacity the capacity
|
* @param capacity the capacity
|
||||||
*/
|
*/
|
||||||
public ByteArrayBuffer(int capacity) {
|
public ByteArrayBuffer(final int capacity) {
|
||||||
super();
|
super();
|
||||||
if (capacity < 0) {
|
|
||||||
throw new IllegalArgumentException("Buffer capacity may not be negative");
|
|
||||||
}
|
|
||||||
this.buffer = new byte[capacity];
|
this.buffer = new byte[capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expand(int newlen) {
|
private void expand(final int newlen) {
|
||||||
byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)];
|
final byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)];
|
||||||
System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
|
System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
|
||||||
this.buffer = newbuffer;
|
this.buffer = newbuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>len</code> bytes to this buffer from the given source
|
* Appends {@code len} bytes to this buffer from the given source
|
||||||
* array starting at index <code>off</code>. The capacity of the buffer
|
* array starting at index {@code off}. The capacity of the buffer
|
||||||
* is increased, if necessary, to accommodate all <code>len</code> bytes.
|
* is increased, if necessary, to accommodate all {@code len} bytes.
|
||||||
*
|
*
|
||||||
* @param b the bytes to be appended.
|
* @param b the bytes to be appended.
|
||||||
* @param off the index of the first byte to append.
|
* @param off the index of the first byte to append.
|
||||||
* @param len the number of bytes to append.
|
* @param len the number of bytes to append.
|
||||||
* @throws IndexOutOfBoundsException if <code>off</code> if out of
|
* @throws IndexOutOfBoundsException if {@code off} if out of
|
||||||
* range, <code>len</code> is negative, or
|
* range, {@code len} is negative, or
|
||||||
* <code>off</code> + <code>len</code> is out of range.
|
* {@code off} + {@code len} is out of range.
|
||||||
*/
|
*/
|
||||||
public void append(final byte[] b, int off, int len) {
|
public void append(final byte[] b, final int off, final int len) {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -85,7 +81,7 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int newlen = this.len + len;
|
final int newlen = this.len + len;
|
||||||
if (newlen > this.buffer.length) {
|
if (newlen > this.buffer.length) {
|
||||||
expand(newlen);
|
expand(newlen);
|
||||||
}
|
}
|
||||||
@ -94,13 +90,13 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>b</code> byte to this buffer. The capacity of the buffer
|
* Appends {@code b} byte to this buffer. The capacity of the buffer
|
||||||
* is increased, if necessary, to accommodate the additional byte.
|
* is increased, if necessary, to accommodate the additional byte.
|
||||||
*
|
*
|
||||||
* @param b the byte to be appended.
|
* @param b the byte to be appended.
|
||||||
*/
|
*/
|
||||||
public void append(int b) {
|
public void append(final int b) {
|
||||||
int newlen = this.len + 1;
|
final int newlen = this.len + 1;
|
||||||
if (newlen > this.buffer.length) {
|
if (newlen > this.buffer.length) {
|
||||||
expand(newlen);
|
expand(newlen);
|
||||||
}
|
}
|
||||||
@ -109,20 +105,20 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>len</code> chars to this buffer from the given source
|
* Appends {@code len} chars to this buffer from the given source
|
||||||
* array starting at index <code>off</code>. The capacity of the buffer
|
* array starting at index {@code off}. The capacity of the buffer
|
||||||
* is increased if necessary to accommodate all <code>len</code> chars.
|
* is increased if necessary to accommodate all {@code len} chars.
|
||||||
* <p>
|
* <p>
|
||||||
* The chars are converted to bytes using simple cast.
|
* The chars are converted to bytes using simple cast.
|
||||||
*
|
*
|
||||||
* @param b the chars to be appended.
|
* @param b the chars to be appended.
|
||||||
* @param off the index of the first char to append.
|
* @param off the index of the first char to append.
|
||||||
* @param len the number of bytes to append.
|
* @param len the number of bytes to append.
|
||||||
* @throws IndexOutOfBoundsException if <code>off</code> if out of
|
* @throws IndexOutOfBoundsException if {@code off} if out of
|
||||||
* range, <code>len</code> is negative, or
|
* range, {@code len} is negative, or
|
||||||
* <code>off</code> + <code>len</code> is out of range.
|
* {@code off} + {@code len} is out of range.
|
||||||
*/
|
*/
|
||||||
public void append(final char[] b, int off, int len) {
|
public void append(final char[] b, final int off, final int len) {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -133,8 +129,8 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int oldlen = this.len;
|
final int oldlen = this.len;
|
||||||
int newlen = oldlen + len;
|
final int newlen = oldlen + len;
|
||||||
if (newlen > this.buffer.length) {
|
if (newlen > this.buffer.length) {
|
||||||
expand(newlen);
|
expand(newlen);
|
||||||
}
|
}
|
||||||
@ -145,21 +141,21 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>len</code> chars to this buffer from the given source
|
* Appends {@code len} chars to this buffer from the given source
|
||||||
* char array buffer starting at index <code>off</code>. The capacity
|
* char array buffer starting at index {@code off}. The capacity
|
||||||
* of the buffer is increased if necessary to accommodate all
|
* of the buffer is increased if necessary to accommodate all
|
||||||
* <code>len</code> chars.
|
* {@code len} chars.
|
||||||
* <p>
|
* <p>
|
||||||
* The chars are converted to bytes using simple cast.
|
* The chars are converted to bytes using simple cast.
|
||||||
*
|
*
|
||||||
* @param b the chars to be appended.
|
* @param b the chars to be appended.
|
||||||
* @param off the index of the first char to append.
|
* @param off the index of the first char to append.
|
||||||
* @param len the number of bytes to append.
|
* @param len the number of bytes to append.
|
||||||
* @throws IndexOutOfBoundsException if <code>off</code> if out of
|
* @throws IndexOutOfBoundsException if {@code off} if out of
|
||||||
* range, <code>len</code> is negative, or
|
* range, {@code len} is negative, or
|
||||||
* <code>off</code> + <code>len</code> is out of range.
|
* {@code off} + {@code len} is out of range.
|
||||||
*/
|
*/
|
||||||
public void append(final CharArrayBuffer b, int off, int len) {
|
public void append(final CharArrayBuffer b, final int off, final int len) {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -179,7 +175,7 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
* @return byte array
|
* @return byte array
|
||||||
*/
|
*/
|
||||||
public byte[] toByteArray() {
|
public byte[] toByteArray() {
|
||||||
byte[] b = new byte[this.len];
|
final byte[] b = new byte[this.len];
|
||||||
if (this.len > 0) {
|
if (this.len > 0) {
|
||||||
System.arraycopy(this.buffer, 0, b, 0, this.len);
|
System.arraycopy(this.buffer, 0, b, 0, this.len);
|
||||||
}
|
}
|
||||||
@ -187,16 +183,16 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the <code>byte</code> value in this buffer at the specified
|
* Returns the {@code byte} value in this buffer at the specified
|
||||||
* index. The index argument must be greater than or equal to
|
* index. The index argument must be greater than or equal to
|
||||||
* <code>0</code>, and less than the length of this buffer.
|
* {@code 0}, and less than the length of this buffer.
|
||||||
*
|
*
|
||||||
* @param i the index of the desired byte value.
|
* @param i the index of the desired byte value.
|
||||||
* @return the byte value at the specified index.
|
* @return the byte value at the specified index.
|
||||||
* @throws IndexOutOfBoundsException if <code>index</code> is
|
* @throws IndexOutOfBoundsException if {@code index} is
|
||||||
* negative or greater than or equal to {@link #length()}.
|
* negative or greater than or equal to {@link #length()}.
|
||||||
*/
|
*/
|
||||||
public int byteAt(int i) {
|
public int byteAt(final int i) {
|
||||||
return this.buffer[i];
|
return this.buffer[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,18 +219,18 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Ensures that the capacity is at least equal to the specified minimum.
|
* Ensures that the capacity is at least equal to the specified minimum.
|
||||||
* If the current capacity is less than the argument, then a new internal
|
* If the current capacity is less than the argument, then a new internal
|
||||||
* array is allocated with greater capacity. If the <code>required</code>
|
* array is allocated with greater capacity. If the {@code required}
|
||||||
* argument is non-positive, this method takes no action.
|
* argument is non-positive, this method takes no action.
|
||||||
*
|
*
|
||||||
* @param required the minimum required capacity.
|
* @param required the minimum required capacity.
|
||||||
*
|
*
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public void ensureCapacity(int required) {
|
public void ensureCapacity(final int required) {
|
||||||
if (required <= 0) {
|
if (required <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int available = this.buffer.length - this.len;
|
final int available = this.buffer.length - this.len;
|
||||||
if (required > available) {
|
if (required > available) {
|
||||||
expand(this.len + required);
|
expand(this.len + required);
|
||||||
}
|
}
|
||||||
@ -252,14 +248,14 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Sets the length of the buffer. The new length value is expected to be
|
* Sets the length of the buffer. The new length value is expected to be
|
||||||
* less than the current capacity and greater than or equal to
|
* less than the current capacity and greater than or equal to
|
||||||
* <code>0</code>.
|
* {@code 0}.
|
||||||
*
|
*
|
||||||
* @param len the new length
|
* @param len the new length
|
||||||
* @throws IndexOutOfBoundsException if the
|
* @throws IndexOutOfBoundsException if the
|
||||||
* <code>len</code> argument is greater than the current
|
* {@code len} argument is greater than the current
|
||||||
* capacity of the buffer or less than <code>0</code>.
|
* capacity of the buffer or less than {@code 0}.
|
||||||
*/
|
*/
|
||||||
public void setLength(int len) {
|
public void setLength(final int len) {
|
||||||
if (len < 0 || len > this.buffer.length) {
|
if (len < 0 || len > this.buffer.length) {
|
||||||
throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
|
throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
|
||||||
}
|
}
|
||||||
@ -267,9 +263,9 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <code>true</code> if this buffer is empty, that is, its
|
* Returns {@code true} if this buffer is empty, that is, its
|
||||||
* {@link #length()} is equal to <code>0</code>.
|
* {@link #length()} is equal to {@code 0}.
|
||||||
* @return <code>true</code> if this buffer is empty, <code>false</code>
|
* @return {@code true} if this buffer is empty, {@code false}
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
@ -277,9 +273,9 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <code>true</code> if this buffer is full, that is, its
|
* Returns {@code true} if this buffer is full, that is, its
|
||||||
* {@link #length()} is equal to its {@link #capacity()}.
|
* {@link #length()} is equal to its {@link #capacity()}.
|
||||||
* @return <code>true</code> if this buffer is full, <code>false</code>
|
* @return {@code true} if this buffer is full, {@code false}
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isFull() {
|
public boolean isFull() {
|
||||||
@ -289,30 +285,32 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Returns the index within this buffer of the first occurrence of the
|
* Returns the index within this buffer of the first occurrence of the
|
||||||
* specified byte, starting the search at the specified
|
* specified byte, starting the search at the specified
|
||||||
* <code>beginIndex</code> and finishing at <code>endIndex</code>.
|
* {@code beginIndex} and finishing at {@code endIndex}.
|
||||||
* If no such byte occurs in this buffer within the specified bounds,
|
* If no such byte occurs in this buffer within the specified bounds,
|
||||||
* <code>-1</code> is returned.
|
* {@code -1} is returned.
|
||||||
* <p>
|
* <p>
|
||||||
* There is no restriction on the value of <code>beginIndex</code> and
|
* There is no restriction on the value of {@code beginIndex} and
|
||||||
* <code>endIndex</code>. If <code>beginIndex</code> is negative,
|
* {@code endIndex}. If {@code beginIndex} is negative,
|
||||||
* it has the same effect as if it were zero. If <code>endIndex</code> is
|
* it has the same effect as if it were zero. If {@code endIndex} is
|
||||||
* greater than {@link #length()}, it has the same effect as if it were
|
* greater than {@link #length()}, it has the same effect as if it were
|
||||||
* {@link #length()}. If the <code>beginIndex</code> is greater than
|
* {@link #length()}. If the {@code beginIndex} is greater than
|
||||||
* the <code>endIndex</code>, <code>-1</code> is returned.
|
* the {@code endIndex}, {@code -1} is returned.
|
||||||
*
|
*
|
||||||
* @param b the byte to search for.
|
* @param b the byte to search for.
|
||||||
* @param beginIndex the index to start the search from.
|
* @param from the index to start the search from.
|
||||||
* @param endIndex the index to finish the search at.
|
* @param to the index to finish the search at.
|
||||||
* @return the index of the first occurrence of the byte in the buffer
|
* @return the index of the first occurrence of the byte in the buffer
|
||||||
* within the given bounds, or <code>-1</code> if the byte does
|
* within the given bounds, or {@code -1} if the byte does
|
||||||
* not occur.
|
* not occur.
|
||||||
*
|
*
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public int indexOf(byte b, int beginIndex, int endIndex) {
|
public int indexOf(final byte b, final int from, final int to) {
|
||||||
|
int beginIndex = from;
|
||||||
if (beginIndex < 0) {
|
if (beginIndex < 0) {
|
||||||
beginIndex = 0;
|
beginIndex = 0;
|
||||||
}
|
}
|
||||||
|
int endIndex = to;
|
||||||
if (endIndex > this.len) {
|
if (endIndex > this.len) {
|
||||||
endIndex = this.len;
|
endIndex = this.len;
|
||||||
}
|
}
|
||||||
@ -329,17 +327,17 @@ public final class ByteArrayBuffer implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index within this buffer of the first occurrence of the
|
* Returns the index within this buffer of the first occurrence of the
|
||||||
* specified byte, starting the search at <code>0</code> and finishing
|
* specified byte, starting the search at {@code 0} and finishing
|
||||||
* at {@link #length()}. If no such byte occurs in this buffer within
|
* at {@link #length()}. If no such byte occurs in this buffer within
|
||||||
* those bounds, <code>-1</code> is returned.
|
* those bounds, {@code -1} is returned.
|
||||||
*
|
*
|
||||||
* @param b the byte to search for.
|
* @param b the byte to search for.
|
||||||
* @return the index of the first occurrence of the byte in the
|
* @return the index of the first occurrence of the byte in the
|
||||||
* buffer, or <code>-1</code> if the byte does not occur.
|
* buffer, or {@code -1} if the byte does not occur.
|
||||||
*
|
*
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public int indexOf(byte b) {
|
public int indexOf(final byte b) {
|
||||||
return indexOf(b, 0, this.len);
|
return indexOf(b, 0, this.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,16 +25,20 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.foxinmy.weixin4j.http.apache;
|
package com.foxinmy.weixin4j.util;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.http.HTTP;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resizable char array.
|
* A resizable char array.
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
public final class CharArrayBuffer implements Serializable {
|
public final class CharArrayBuffer implements CharSequence, Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = -6208952725094867135L;
|
private static final long serialVersionUID = -6208952725094867135L;
|
||||||
|
|
||||||
@ -47,33 +51,30 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
*
|
*
|
||||||
* @param capacity the capacity
|
* @param capacity the capacity
|
||||||
*/
|
*/
|
||||||
public CharArrayBuffer(int capacity) {
|
public CharArrayBuffer(final int capacity) {
|
||||||
super();
|
super();
|
||||||
if (capacity < 0) {
|
|
||||||
throw new IllegalArgumentException("Buffer capacity may not be negative");
|
|
||||||
}
|
|
||||||
this.buffer = new char[capacity];
|
this.buffer = new char[capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expand(int newlen) {
|
private void expand(final int newlen) {
|
||||||
char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
|
final char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
|
||||||
System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
|
System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
|
||||||
this.buffer = newbuffer;
|
this.buffer = newbuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>len</code> chars to this buffer from the given source
|
* Appends {@code len} chars to this buffer from the given source
|
||||||
* array starting at index <code>off</code>. The capacity of the buffer
|
* array starting at index {@code off}. The capacity of the buffer
|
||||||
* is increased, if necessary, to accommodate all <code>len</code> chars.
|
* is increased, if necessary, to accommodate all {@code len} chars.
|
||||||
*
|
*
|
||||||
* @param b the chars to be appended.
|
* @param b the chars to be appended.
|
||||||
* @param off the index of the first char to append.
|
* @param off the index of the first char to append.
|
||||||
* @param len the number of chars to append.
|
* @param len the number of chars to append.
|
||||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||||
* range, <code>len</code> is negative, or
|
* range, {@code len} is negative, or
|
||||||
* <code>off</code> + <code>len</code> is out of range.
|
* {@code off} + {@code len} is out of range.
|
||||||
*/
|
*/
|
||||||
public void append(final char[] b, int off, int len) {
|
public void append(final char[] b, final int off, final int len) {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -84,7 +85,7 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int newlen = this.len + len;
|
final int newlen = this.len + len;
|
||||||
if (newlen > this.buffer.length) {
|
if (newlen > this.buffer.length) {
|
||||||
expand(newlen);
|
expand(newlen);
|
||||||
}
|
}
|
||||||
@ -98,33 +99,31 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
*
|
*
|
||||||
* @param str the string.
|
* @param str the string.
|
||||||
*/
|
*/
|
||||||
public void append(String str) {
|
public void append(final String str) {
|
||||||
if (str == null) {
|
final String s = str != null ? str : "null";
|
||||||
str = "null";
|
final int strlen = s.length();
|
||||||
}
|
final int newlen = this.len + strlen;
|
||||||
int strlen = str.length();
|
|
||||||
int newlen = this.len + strlen;
|
|
||||||
if (newlen > this.buffer.length) {
|
if (newlen > this.buffer.length) {
|
||||||
expand(newlen);
|
expand(newlen);
|
||||||
}
|
}
|
||||||
str.getChars(0, strlen, this.buffer, this.len);
|
s.getChars(0, strlen, this.buffer, this.len);
|
||||||
this.len = newlen;
|
this.len = newlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>len</code> chars to this buffer from the given source
|
* Appends {@code len} chars to this buffer from the given source
|
||||||
* buffer starting at index <code>off</code>. The capacity of the
|
* buffer starting at index {@code off}. The capacity of the
|
||||||
* destination buffer is increased, if necessary, to accommodate all
|
* destination buffer is increased, if necessary, to accommodate all
|
||||||
* <code>len</code> chars.
|
* {@code len} chars.
|
||||||
*
|
*
|
||||||
* @param b the source buffer to be appended.
|
* @param b the source buffer to be appended.
|
||||||
* @param off the index of the first char to append.
|
* @param off the index of the first char to append.
|
||||||
* @param len the number of chars to append.
|
* @param len the number of chars to append.
|
||||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||||
* range, <code>len</code> is negative, or
|
* range, {@code len} is negative, or
|
||||||
* <code>off</code> + <code>len</code> is out of range.
|
* {@code off} + {@code len} is out of range.
|
||||||
*/
|
*/
|
||||||
public void append(final CharArrayBuffer b, int off, int len) {
|
public void append(final CharArrayBuffer b, final int off, final int len) {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -133,7 +132,7 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends all chars to this buffer from the given source buffer starting
|
* Appends all chars to this buffer from the given source buffer starting
|
||||||
* at index <code>0</code>. The capacity of the destination buffer is
|
* at index {@code 0}. The capacity of the destination buffer is
|
||||||
* increased, if necessary, to accommodate all {@link #length()} chars.
|
* increased, if necessary, to accommodate all {@link #length()} chars.
|
||||||
*
|
*
|
||||||
* @param b the source buffer to be appended.
|
* @param b the source buffer to be appended.
|
||||||
@ -146,13 +145,13 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>ch</code> char to this buffer. The capacity of the buffer
|
* Appends {@code ch} char to this buffer. The capacity of the buffer
|
||||||
* is increased, if necessary, to accommodate the additional char.
|
* is increased, if necessary, to accommodate the additional char.
|
||||||
*
|
*
|
||||||
* @param ch the char to be appended.
|
* @param ch the char to be appended.
|
||||||
*/
|
*/
|
||||||
public void append(char ch) {
|
public void append(final char ch) {
|
||||||
int newlen = this.len + 1;
|
final int newlen = this.len + 1;
|
||||||
if (newlen > this.buffer.length) {
|
if (newlen > this.buffer.length) {
|
||||||
expand(newlen);
|
expand(newlen);
|
||||||
}
|
}
|
||||||
@ -161,20 +160,20 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>len</code> bytes to this buffer from the given source
|
* Appends {@code len} bytes to this buffer from the given source
|
||||||
* array starting at index <code>off</code>. The capacity of the buffer
|
* array starting at index {@code off}. The capacity of the buffer
|
||||||
* is increased, if necessary, to accommodate all <code>len</code> bytes.
|
* is increased, if necessary, to accommodate all {@code len} bytes.
|
||||||
* <p>
|
* <p>
|
||||||
* The bytes are converted to chars using simple cast.
|
* The bytes are converted to chars using simple cast.
|
||||||
*
|
*
|
||||||
* @param b the bytes to be appended.
|
* @param b the bytes to be appended.
|
||||||
* @param off the index of the first byte to append.
|
* @param off the index of the first byte to append.
|
||||||
* @param len the number of bytes to append.
|
* @param len the number of bytes to append.
|
||||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||||
* range, <code>len</code> is negative, or
|
* range, {@code len} is negative, or
|
||||||
* <code>off</code> + <code>len</code> is out of range.
|
* {@code off} + {@code len} is out of range.
|
||||||
*/
|
*/
|
||||||
public void append(final byte[] b, int off, int len) {
|
public void append(final byte[] b, final int off, final int len) {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -185,8 +184,8 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int oldlen = this.len;
|
final int oldlen = this.len;
|
||||||
int newlen = oldlen + len;
|
final int newlen = oldlen + len;
|
||||||
if (newlen > this.buffer.length) {
|
if (newlen > this.buffer.length) {
|
||||||
expand(newlen);
|
expand(newlen);
|
||||||
}
|
}
|
||||||
@ -197,20 +196,20 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends <code>len</code> bytes to this buffer from the given source
|
* Appends {@code len} bytes to this buffer from the given source
|
||||||
* array starting at index <code>off</code>. The capacity of the buffer
|
* array starting at index {@code off}. The capacity of the buffer
|
||||||
* is increased, if necessary, to accommodate all <code>len</code> bytes.
|
* is increased, if necessary, to accommodate all {@code len} bytes.
|
||||||
* <p>
|
* <p>
|
||||||
* The bytes are converted to chars using simple cast.
|
* The bytes are converted to chars using simple cast.
|
||||||
*
|
*
|
||||||
* @param b the bytes to be appended.
|
* @param b the bytes to be appended.
|
||||||
* @param off the index of the first byte to append.
|
* @param off the index of the first byte to append.
|
||||||
* @param len the number of bytes to append.
|
* @param len the number of bytes to append.
|
||||||
* @throws IndexOutOfBoundsException if <code>off</code> is out of
|
* @throws IndexOutOfBoundsException if {@code off} is out of
|
||||||
* range, <code>len</code> is negative, or
|
* range, {@code len} is negative, or
|
||||||
* <code>off</code> + <code>len</code> is out of range.
|
* {@code off} + {@code len} is out of range.
|
||||||
*/
|
*/
|
||||||
public void append(final ByteArrayBuffer b, int off, int len) {
|
public void append(final ByteArrayBuffer b, final int off, final int len) {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -241,7 +240,7 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
* @return char array
|
* @return char array
|
||||||
*/
|
*/
|
||||||
public char[] toCharArray() {
|
public char[] toCharArray() {
|
||||||
char[] b = new char[this.len];
|
final char[] b = new char[this.len];
|
||||||
if (this.len > 0) {
|
if (this.len > 0) {
|
||||||
System.arraycopy(this.buffer, 0, b, 0, this.len);
|
System.arraycopy(this.buffer, 0, b, 0, this.len);
|
||||||
}
|
}
|
||||||
@ -249,16 +248,17 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the <code>char</code> value in this buffer at the specified
|
* Returns the {@code char} value in this buffer at the specified
|
||||||
* index. The index argument must be greater than or equal to
|
* index. The index argument must be greater than or equal to
|
||||||
* <code>0</code>, and less than the length of this buffer.
|
* {@code 0}, and less than the length of this buffer.
|
||||||
*
|
*
|
||||||
* @param i the index of the desired char value.
|
* @param i the index of the desired char value.
|
||||||
* @return the char value at the specified index.
|
* @return the char value at the specified index.
|
||||||
* @throws IndexOutOfBoundsException if <code>index</code> is
|
* @throws IndexOutOfBoundsException if {@code index} is
|
||||||
* negative or greater than or equal to {@link #length()}.
|
* negative or greater than or equal to {@link #length()}.
|
||||||
*/
|
*/
|
||||||
public char charAt(int i) {
|
@Override
|
||||||
|
public char charAt(final int i) {
|
||||||
return this.buffer[i];
|
return this.buffer[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,6 +287,7 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
*
|
*
|
||||||
* @return the length of the buffer
|
* @return the length of the buffer
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public int length() {
|
public int length() {
|
||||||
return this.len;
|
return this.len;
|
||||||
}
|
}
|
||||||
@ -294,16 +295,16 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Ensures that the capacity is at least equal to the specified minimum.
|
* Ensures that the capacity is at least equal to the specified minimum.
|
||||||
* If the current capacity is less than the argument, then a new internal
|
* If the current capacity is less than the argument, then a new internal
|
||||||
* array is allocated with greater capacity. If the <code>required</code>
|
* array is allocated with greater capacity. If the {@code required}
|
||||||
* argument is non-positive, this method takes no action.
|
* argument is non-positive, this method takes no action.
|
||||||
*
|
*
|
||||||
* @param required the minimum required capacity.
|
* @param required the minimum required capacity.
|
||||||
*/
|
*/
|
||||||
public void ensureCapacity(int required) {
|
public void ensureCapacity(final int required) {
|
||||||
if (required <= 0) {
|
if (required <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int available = this.buffer.length - this.len;
|
final int available = this.buffer.length - this.len;
|
||||||
if (required > available) {
|
if (required > available) {
|
||||||
expand(this.len + required);
|
expand(this.len + required);
|
||||||
}
|
}
|
||||||
@ -312,14 +313,14 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Sets the length of the buffer. The new length value is expected to be
|
* Sets the length of the buffer. The new length value is expected to be
|
||||||
* less than the current capacity and greater than or equal to
|
* less than the current capacity and greater than or equal to
|
||||||
* <code>0</code>.
|
* {@code 0}.
|
||||||
*
|
*
|
||||||
* @param len the new length
|
* @param len the new length
|
||||||
* @throws IndexOutOfBoundsException if the
|
* @throws IndexOutOfBoundsException if the
|
||||||
* <code>len</code> argument is greater than the current
|
* {@code len} argument is greater than the current
|
||||||
* capacity of the buffer or less than <code>0</code>.
|
* capacity of the buffer or less than {@code 0}.
|
||||||
*/
|
*/
|
||||||
public void setLength(int len) {
|
public void setLength(final int len) {
|
||||||
if (len < 0 || len > this.buffer.length) {
|
if (len < 0 || len > this.buffer.length) {
|
||||||
throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
|
throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
|
||||||
}
|
}
|
||||||
@ -327,9 +328,9 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <code>true</code> if this buffer is empty, that is, its
|
* Returns {@code true} if this buffer is empty, that is, its
|
||||||
* {@link #length()} is equal to <code>0</code>.
|
* {@link #length()} is equal to {@code 0}.
|
||||||
* @return <code>true</code> if this buffer is empty, <code>false</code>
|
* @return {@code true} if this buffer is empty, {@code false}
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
@ -337,9 +338,9 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <code>true</code> if this buffer is full, that is, its
|
* Returns {@code true} if this buffer is full, that is, its
|
||||||
* {@link #length()} is equal to its {@link #capacity()}.
|
* {@link #length()} is equal to its {@link #capacity()}.
|
||||||
* @return <code>true</code> if this buffer is full, <code>false</code>
|
* @return {@code true} if this buffer is full, {@code false}
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isFull() {
|
public boolean isFull() {
|
||||||
@ -349,28 +350,30 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Returns the index within this buffer of the first occurrence of the
|
* Returns the index within this buffer of the first occurrence of the
|
||||||
* specified character, starting the search at the specified
|
* specified character, starting the search at the specified
|
||||||
* <code>beginIndex</code> and finishing at <code>endIndex</code>.
|
* {@code beginIndex} and finishing at {@code endIndex}.
|
||||||
* If no such character occurs in this buffer within the specified bounds,
|
* If no such character occurs in this buffer within the specified bounds,
|
||||||
* <code>-1</code> is returned.
|
* {@code -1} is returned.
|
||||||
* <p>
|
* <p>
|
||||||
* There is no restriction on the value of <code>beginIndex</code> and
|
* There is no restriction on the value of {@code beginIndex} and
|
||||||
* <code>endIndex</code>. If <code>beginIndex</code> is negative,
|
* {@code endIndex}. If {@code beginIndex} is negative,
|
||||||
* it has the same effect as if it were zero. If <code>endIndex</code> is
|
* it has the same effect as if it were zero. If {@code endIndex} is
|
||||||
* greater than {@link #length()}, it has the same effect as if it were
|
* greater than {@link #length()}, it has the same effect as if it were
|
||||||
* {@link #length()}. If the <code>beginIndex</code> is greater than
|
* {@link #length()}. If the {@code beginIndex} is greater than
|
||||||
* the <code>endIndex</code>, <code>-1</code> is returned.
|
* the {@code endIndex}, {@code -1} is returned.
|
||||||
*
|
*
|
||||||
* @param ch the char to search for.
|
* @param ch the char to search for.
|
||||||
* @param beginIndex the index to start the search from.
|
* @param from the index to start the search from.
|
||||||
* @param endIndex the index to finish the search at.
|
* @param to the index to finish the search at.
|
||||||
* @return the index of the first occurrence of the character in the buffer
|
* @return the index of the first occurrence of the character in the buffer
|
||||||
* within the given bounds, or <code>-1</code> if the character does
|
* within the given bounds, or {@code -1} if the character does
|
||||||
* not occur.
|
* not occur.
|
||||||
*/
|
*/
|
||||||
public int indexOf(int ch, int beginIndex, int endIndex) {
|
public int indexOf(final int ch, final int from, final int to) {
|
||||||
|
int beginIndex = from;
|
||||||
if (beginIndex < 0) {
|
if (beginIndex < 0) {
|
||||||
beginIndex = 0;
|
beginIndex = 0;
|
||||||
}
|
}
|
||||||
|
int endIndex = to;
|
||||||
if (endIndex > this.len) {
|
if (endIndex > this.len) {
|
||||||
endIndex = this.len;
|
endIndex = this.len;
|
||||||
}
|
}
|
||||||
@ -387,69 +390,98 @@ public final class CharArrayBuffer implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index within this buffer of the first occurrence of the
|
* Returns the index within this buffer of the first occurrence of the
|
||||||
* specified character, starting the search at <code>0</code> and finishing
|
* specified character, starting the search at {@code 0} and finishing
|
||||||
* at {@link #length()}. If no such character occurs in this buffer within
|
* at {@link #length()}. If no such character occurs in this buffer within
|
||||||
* those bounds, <code>-1</code> is returned.
|
* those bounds, {@code -1} is returned.
|
||||||
*
|
*
|
||||||
* @param ch the char to search for.
|
* @param ch the char to search for.
|
||||||
* @return the index of the first occurrence of the character in the
|
* @return the index of the first occurrence of the character in the
|
||||||
* buffer, or <code>-1</code> if the character does not occur.
|
* buffer, or {@code -1} if the character does not occur.
|
||||||
*/
|
*/
|
||||||
public int indexOf(int ch) {
|
public int indexOf(final int ch) {
|
||||||
return indexOf(ch, 0, this.len);
|
return indexOf(ch, 0, this.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a substring of this buffer. The substring begins at the specified
|
* Returns a substring of this buffer. The substring begins at the specified
|
||||||
* <code>beginIndex</code> and extends to the character at index
|
* {@code beginIndex} and extends to the character at index
|
||||||
* <code>endIndex - 1</code>.
|
* {@code endIndex - 1}.
|
||||||
*
|
*
|
||||||
* @param beginIndex the beginning index, inclusive.
|
* @param beginIndex the beginning index, inclusive.
|
||||||
* @param endIndex the ending index, exclusive.
|
* @param endIndex the ending index, exclusive.
|
||||||
* @return the specified substring.
|
* @return the specified substring.
|
||||||
* @exception StringIndexOutOfBoundsException if the
|
* @throws StringIndexOutOfBoundsException if the
|
||||||
* <code>beginIndex</code> is negative, or
|
* {@code beginIndex} is negative, or
|
||||||
* <code>endIndex</code> is larger than the length of this
|
* {@code endIndex} is larger than the length of this
|
||||||
* buffer, or <code>beginIndex</code> is larger than
|
* buffer, or {@code beginIndex} is larger than
|
||||||
* <code>endIndex</code>.
|
* {@code endIndex}.
|
||||||
*/
|
*/
|
||||||
public String substring(int beginIndex, int endIndex) {
|
public String substring(final int beginIndex, final int endIndex) {
|
||||||
|
if (beginIndex < 0) {
|
||||||
|
throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex);
|
||||||
|
}
|
||||||
|
if (endIndex > this.len) {
|
||||||
|
throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len);
|
||||||
|
}
|
||||||
|
if (beginIndex > endIndex) {
|
||||||
|
throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex);
|
||||||
|
}
|
||||||
return new String(this.buffer, beginIndex, endIndex - beginIndex);
|
return new String(this.buffer, beginIndex, endIndex - beginIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a substring of this buffer with leading and trailing whitespace
|
* Returns a substring of this buffer with leading and trailing whitespace
|
||||||
* omitted. The substring begins with the first non-whitespace character
|
* omitted. The substring begins with the first non-whitespace character
|
||||||
* from <code>beginIndex</code> and extends to the last
|
* from {@code beginIndex} and extends to the last
|
||||||
* non-whitespace character with the index lesser than
|
* non-whitespace character with the index lesser than
|
||||||
* <code>endIndex</code>.
|
* {@code endIndex}.
|
||||||
*
|
*
|
||||||
* @param beginIndex the beginning index, inclusive.
|
* @param beginIndex the beginning index, inclusive.
|
||||||
* @param endIndex the ending index, exclusive.
|
* @param endIndex the ending index, exclusive.
|
||||||
* @return the specified substring.
|
* @return the specified substring.
|
||||||
* @exception IndexOutOfBoundsException if the
|
* @throws IndexOutOfBoundsException if the
|
||||||
* <code>beginIndex</code> is negative, or
|
* {@code beginIndex} is negative, or
|
||||||
* <code>endIndex</code> is larger than the length of this
|
* {@code endIndex} is larger than the length of this
|
||||||
* buffer, or <code>beginIndex</code> is larger than
|
* buffer, or {@code beginIndex} is larger than
|
||||||
* <code>endIndex</code>.
|
* {@code endIndex}.
|
||||||
*/
|
*/
|
||||||
public String substringTrimmed(int beginIndex, int endIndex) {
|
public String substringTrimmed(final int beginIndex, final int endIndex) {
|
||||||
if (beginIndex < 0) {
|
if (beginIndex < 0) {
|
||||||
throw new IndexOutOfBoundsException("Negative beginIndex: "+beginIndex);
|
throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex);
|
||||||
}
|
}
|
||||||
if (endIndex > this.len) {
|
if (endIndex > this.len) {
|
||||||
throw new IndexOutOfBoundsException("endIndex: "+endIndex+" > length: "+this.len);
|
throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len);
|
||||||
}
|
}
|
||||||
if (beginIndex > endIndex) {
|
if (beginIndex > endIndex) {
|
||||||
throw new IndexOutOfBoundsException("beginIndex: "+beginIndex+" > endIndex: "+endIndex);
|
throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex);
|
||||||
}
|
}
|
||||||
while (beginIndex < endIndex && HTTP.isWhitespace(this.buffer[beginIndex])) {
|
int beginIndex0 = beginIndex;
|
||||||
beginIndex++;
|
int endIndex0 = endIndex;
|
||||||
|
while (beginIndex0 < endIndex && HTTP.isWhitespace(this.buffer[beginIndex0])) {
|
||||||
|
beginIndex0++;
|
||||||
}
|
}
|
||||||
while (endIndex > beginIndex && HTTP.isWhitespace(this.buffer[endIndex - 1])) {
|
while (endIndex0 > beginIndex0 && HTTP.isWhitespace(this.buffer[endIndex0 - 1])) {
|
||||||
endIndex--;
|
endIndex0--;
|
||||||
}
|
}
|
||||||
return new String(this.buffer, beginIndex, endIndex - beginIndex);
|
return new String(this.buffer, beginIndex0, endIndex0 - beginIndex0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CharSequence subSequence(final int beginIndex, final int endIndex) {
|
||||||
|
if (beginIndex < 0) {
|
||||||
|
throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex);
|
||||||
|
}
|
||||||
|
if (endIndex > this.len) {
|
||||||
|
throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len);
|
||||||
|
}
|
||||||
|
if (beginIndex > endIndex) {
|
||||||
|
throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex);
|
||||||
|
}
|
||||||
|
return CharBuffer.wrap(this.buffer, beginIndex, endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -10,8 +10,8 @@ import com.alibaba.fastjson.JSONObject;
|
|||||||
import com.alibaba.fastjson.TypeReference;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
import com.foxinmy.weixin4j.http.MimeType;
|
import com.foxinmy.weixin4j.http.MimeType;
|
||||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||||
import com.foxinmy.weixin4j.http.apache.InputStreamBody;
|
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||||
import com.foxinmy.weixin4j.model.Token;
|
import com.foxinmy.weixin4j.model.Token;
|
||||||
|
|||||||
@ -19,10 +19,10 @@ import com.foxinmy.weixin4j.http.HttpMethod;
|
|||||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||||
import com.foxinmy.weixin4j.http.MimeType;
|
import com.foxinmy.weixin4j.http.MimeType;
|
||||||
import com.foxinmy.weixin4j.http.apache.ByteArrayBody;
|
import com.foxinmy.weixin4j.http.apache.content.ByteArrayBody;
|
||||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||||
import com.foxinmy.weixin4j.http.apache.InputStreamBody;
|
import com.foxinmy.weixin4j.http.apache.content.StringBody;
|
||||||
import com.foxinmy.weixin4j.http.apache.StringBody;
|
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||||
import com.foxinmy.weixin4j.http.entity.StringEntity;
|
import com.foxinmy.weixin4j.http.entity.StringEntity;
|
||||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||||
|
|||||||
@ -22,9 +22,9 @@ import com.foxinmy.weixin4j.http.HttpMethod;
|
|||||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||||
import com.foxinmy.weixin4j.http.HttpResponse;
|
import com.foxinmy.weixin4j.http.HttpResponse;
|
||||||
import com.foxinmy.weixin4j.http.MimeType;
|
import com.foxinmy.weixin4j.http.MimeType;
|
||||||
import com.foxinmy.weixin4j.http.apache.ByteArrayBody;
|
import com.foxinmy.weixin4j.http.apache.content.ByteArrayBody;
|
||||||
import com.foxinmy.weixin4j.http.apache.FormBodyPart;
|
import com.foxinmy.weixin4j.http.apache.content.InputStreamBody;
|
||||||
import com.foxinmy.weixin4j.http.apache.InputStreamBody;
|
import com.foxinmy.weixin4j.http.apache.mime.FormBodyPart;
|
||||||
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
import com.foxinmy.weixin4j.http.weixin.ApiResult;
|
||||||
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
|
||||||
import com.foxinmy.weixin4j.model.Token;
|
import com.foxinmy.weixin4j.model.Token;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user