public class ClientConfig {
	// cos server的域名地址
    private static final String COS_ENDPOINT = "http://web.file.myqcloud.com/files/v1";
    // 多次签名的默认过期时间,单位秒
    private static final int DEFAULT_SIGN_EXPIRED = 300;
    // 默认的最大重试次数(发生了socketException时)
    private static final int DEFAULT_MAX_RETRIES = 3;
    // 默认的获取连接的超时时间
    private static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = -1;
    // 默认连接超时, 单位ms
    private static final int DEFAULT_CONNECTION_TIMEOUT = 30 * 1000;
    // 默认的SOCKET读取超时时间, 默认毫秒
    private static final int DEFAULT_SOCKET_TIMEOUT = 30 * 1000;
    // 默认的维护最大HTTP连接数
    private static final int DEFAULT_MAX_CONNECTIONS_COUNT = 100;
    // 默认的user_agent标识
    private static final String DEFAULT_USER_AGENT = "cos-java-sdk-v3.3";

    
    private String cosEndPoint = COS_ENDPOINT;
    private int signExpired = DEFAULT_SIGN_EXPIRED;
    private int maxFailedRetry = DEFAULT_MAX_RETRIES;
    private int connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
    private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
    private int socketTimeout = DEFAULT_SOCKET_TIMEOUT;
    private int maxConnectionsCount = DEFAULT_MAX_CONNECTIONS_COUNT;
    private String userAgent = DEFAULT_USER_AGENT;
 

    public int getMaxFailedRetry() {
        return maxFailedRetry;
    }

    public void setMaxFailedRetry(int maxFailedRetry) {
        this.maxFailedRetry = maxFailedRetry;
    }
    

    public int getSignExpired() {
		return signExpired;
	}

	public void setSignExpired(int signExpired) {
		this.signExpired = signExpired;
	}

	public int getConnectionRequestTimeout() {
        return connectionRequestTimeout;
    }

    public void setConnectionRequestTimeout(int connectionRequestTimeout) {
        this.connectionRequestTimeout = connectionRequestTimeout;
    }

    public int getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public int getSocketTimeout() {
        return socketTimeout;
    }

    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    public int getMaxConnectionsCount() {
        return maxConnectionsCount;
    }

    public void setMaxConnectionsCount(int maxConnectionsCount) {
        this.maxConnectionsCount = maxConnectionsCount;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    public String getCosEndPoint() {
        return cosEndPoint;
    }

    public void setCosEndPoint(String cosEndpoint) {
        this.cosEndPoint = cosEndpoint;
    }
    
} public class CommonPathUtils {
	private static final Logger LOG = LoggerFactory.getLogger(CommonPathUtils.class);
	private static final String PATH_DELIMITER = "/";

	public static String encodeRemotePath(String urlPath) throws AbstractCosException {
		StringBuilder pathBuilder = new StringBuilder();
		String[] pathSegmentsArr = urlPath.split(PATH_DELIMITER);

		for (String pathSegment : pathSegmentsArr) {
			if (!pathSegment.isEmpty()) {
				try {
					pathBuilder.append(PATH_DELIMITER).append(URLEncoder.encode(pathSegment, "UTF-8").replace("+", "%20"));
				} catch (UnsupportedEncodingException e) {
					String errMsg = "Unsupported ecnode exception:" + e.toString();
					LOG.error(errMsg);
					throw new UnknownException(errMsg);
				}
			}
		}
		if (urlPath.endsWith(PATH_DELIMITER)) {
			pathBuilder.append(PATH_DELIMITER);
		}
		return pathBuilder.toString();
	}
} public abstract class AbstractCosException extends Exception {

    private static final long serialVersionUID = 7547532865194837136L;
    
    private CosExceptionType type;

    public AbstractCosException(CosExceptionType type, String message) {
        super(message);
        this.type = type;
    }

    public CosExceptionType getType() {
        return type;
    }
    
    @Override
    public String toString() {
        JSONObject responseObj = new JSONObject();
        responseObj.put(ResponseBodyKey.CODE, type.getErrorCode());
        responseObj.put(ResponseBodyKey.MESSAGE, getMessage());
        return responseObj.toString();
    }
    
} public abstract class AbstractCosHttpClient {
    protected ClientConfig config;
    protected HttpClient httpClient;
    
    protected PoolingHttpClientConnectionManager connectionManager;
    protected IdleConnectionMonitorThread idleConnectionMonitor;
    
    protected RequestConfig requestConfig;

    public AbstractCosHttpClient(ClientConfig config) {
        super();
        this.config = config;
        this.connectionManager = new PoolingHttpClientConnectionManager();
        this.connectionManager.setMaxTotal(config.getMaxConnectionsCount());
        this.connectionManager.setDefaultMaxPerRoute(config.getMaxConnectionsCount());
        this.httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
        this.requestConfig = RequestConfig.custom()
                                          .setConnectionRequestTimeout(this.config.getConnectionRequestTimeout())
                                          .setConnectTimeout(this.config.getConnectionTimeout())
                                          .setSocketTimeout(this.config.getSocketTimeout())
                                          .build();
        this.idleConnectionMonitor = new IdleConnectionMonitorThread(this.connectionManager);
        this.idleConnectionMonitor.start();
    }

    protected abstract String sendPostRequest(HttpRequest httpRequest) throws AbstractCosException;

    protected abstract String sendGetRequest(HttpRequest httpRequest) throws AbstractCosException;

    public String sendHttpRequest(HttpRequest httpRequest) throws AbstractCosException {

        HttpMethod method = httpRequest.getMethod();
        if (method == HttpMethod.POST) {
            return sendPostRequest(httpRequest);
        } else if (method == HttpMethod.GET) {
            return sendGetRequest(httpRequest);
        } else {
            throw new ParamException("Unsupported Http Method");
        }
    }
    
    public void shutdown() {
        this.idleConnectionMonitor.shutdown();
    }
} public enum HttpContentType {
    
    APPLICATION_JSON("application/json"), 
    
    MULTIPART_FORM_DATA("multipart/form-data");
    
    private String contentType;

    private HttpContentType(String contentType) {
        this.contentType = contentType;
    }
    
    @Override
    public String toString() {
        return this.contentType;
    }
} public enum HttpMethod {
    
    GET("GET"), 
    
    POST("POST"), 
    
    PUT("PUT"), 
    
    DELETE("DELETE");

    private String method;

    private HttpMethod(String method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return this.method;
    }
} public class HttpRequest {
	private String url = "";
	private HttpMethod method = HttpMethod.POST;
	private HttpContentType contentType = HttpContentType.MULTIPART_FORM_DATA;
	private Map<String, String> headers = new LinkedHashMap<>();
	private Map<String, String> params = new LinkedHashMap<>();

	public HttpRequest() {
		super();
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public HttpMethod getMethod() {
		return method;
	}

	public void setMethod(HttpMethod method) {
		this.method = method;
	}

	public HttpContentType getContentType() {
		return contentType;
	}

	public void setContentType(HttpContentType contentType) {
		this.contentType = contentType;
	}

	public Map<String, String> getHeaders() {
		return headers;
	}

	public Map<String, String> getParams() {
		return params;
	}

	public void addHeader(String key, String value) {
		this.headers.put(key, value);
	}

	public void addParam(String key, String value) {
		this.params.put(key, value);
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("url:").append(url).append(", method:").append(method).append(", ConentType:")
				.append(contentType.toString()).append("\n");

		sb.append("Headers:\n");
		for (Entry<String, String> entry : headers.entrySet()) {
			sb.append("key:").append(entry.getKey());
			sb.append(", value:").append(entry.getValue());
			sb.append("\n");
		}

		sb.append("params:\n");
		for (Entry<String, String> entry : params.entrySet()) {
			sb.append("key:").append(entry.getKey());
			sb.append(", value:").append(entry.getValue());
			sb.append("\n");
		}

		return sb.toString();
	}
} public class RequestBodyKey {
	public static final String OP = "op";
	public static final String BIZ_ATTR = "biz_attr";
	public static final String UPDATE_FLAG = "flag";
	public static final String AUTHORITY = "authority";
	public static final String CUSTOM_HEADERS = "custom_headers";
	public static final String INSERT_ONLY = "insertOnly";
	public static final String DEST_FIELD = "dest_fileid";
	public static final String TO_OVER_WRITE = "to_over_write";
	public static final String PREFIX = "prefix";
	public static final String NUM = "num";
	public static final String PATTERN = "pattern";
	public static final String ORDER = "order";
	public static final String CONTEXT = "context";
	public static final String SHA = "sha";
	public static final String FILE_CONTENT = "fileContent";
	public static final String FILE_SIZE = "filesize";
	public static final String SLICE_SIZE = "slice_size";
	public static final String SESSION = "session";
	public static final String OFFSET = "offset";
	public static final String UPLOAD_PARTS = "uploadparts";

	public class UploadParts {
		public static final String OFFSET = "offset";
		public static final String DATA_LEN = "datalen";
		public static final String DATA_SHA = "datasha";
	}
} public class RequestBodyValue {
    public class OP {
        public static final String CREATE = "create";
        public static final String LIST = "list";
        public static final String UPDATE = "update";
        public static final String STAT = "stat";
        public static final String MOVE = "move";
        public static final String DELETE = "delete";
        public static final String UPLOAD = "upload";
        public static final String UPLOAD_SLICE = "upload_slice";
    }

} public class RequestHeaderKey {
    public static final String Authorization = "Authorization";
    public static final String Content_TYPE = "Content-Type";
    public static final String ACCEPT = "Accept";
    public static final String CONNECTION = "Connection";
    public static final String USER_AGENT = "User-Agent";
} public class RequestHeaderValue {
    public static class Method {
        public static final String POST = "POST";
        public static final String GET = "GET";
        public static final String PUT = "PUT";
        public static final String DELETE = "DELETE";
    }
    
    public static class ContentType {
        public static final String JSON = "application/json";
        public static final String FORM_DATA = "multipart/form-data";
    }
    
    public static class Accept {
        public static final String ALL = "*/*";
    }
    
    public static class UserAgent {
        public static final String COS_FLAG = "qcloud-java-sdk";
    }
    
    public static class Connection {
        public static final String KEEP_ALIVE = "Keep-Alive";
    }
    
} public abstract class AbstractBaseRequest {
    // bucket名
    private String bucketName;
    // cos路径
    private String cosPath;

    public AbstractBaseRequest(String bucketName, String cosPath) {
        super();
        this.bucketName = bucketName;
        this.cosPath = cosPath;
    }

    // 获取bucket名
    public String getBucketName() {
        return bucketName;
    }

    // 设置bucket名
    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }
    
    // 获取cos_path
    public String getCosPath() {
        return cosPath;
    }

    // 设置cos_path
    public void setCosPath(String cosPath) {
        this.cosPath = cosPath;
    }
    
    protected String getMemberStringValue(String member) {
    	if (member == null) {
    		return "null";
    	} else {
    		return member;
    	}
    }

    // 将request转换为字符串, 用于记录信息
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("bucketName:").append(getMemberStringValue(bucketName));
        sb.append(", cosPath:").append(getMemberStringValue(cosPath));
        return sb.toString();
    }
    

    
    // 检查用户的输入参数
    public void check_param() throws ParamException {
    	CommonParamCheckUtils.AssertNotNull("bucketName", this.bucketName);
    	CommonParamCheckUtils.AssertNotNull("cosPath", this.cosPath);
    }
} public class AbstractDelRequest extends AbstractBaseRequest {

    public AbstractDelRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }
    
    @Override
    public void check_param() throws ParamException {
    	super.check_param();
    }
    
    @Override
    public String toString() {
    	return super.toString();
    }
} public class AbstractStatRequest extends AbstractBaseRequest {

    public AbstractStatRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }
    
    @Override
    public void check_param() throws ParamException {
    	super.check_param();
    }
    
    @Override
    public String toString() {
    	return super.toString();
    }

} public class Credentials {
    private final int appId;
    private final String secretId;
    private final String secretKey;

    public Credentials(int appId, String secretId, String secretKey) {
        super();
        this.appId = appId;
        this.secretId = secretId;
        this.secretKey = secretKey;
    }

    public int getAppId() {
        return appId;
    }

    public String getSecretId() {
        return secretId;
    }

    public String getSecretKey() {
        return secretKey;
    }

} public class Sign {

	/**
	 * 返回用户访问资源的签名
	 * 
	 * @param cred
	 *            包含用户秘钥信息
	 * @param bucketName
	 *            bucket名
	 * @param cosPath
	 *            要签名的cos路径
	 * @param expired
	 *            超时时间
	 * @param uploadFlag
	 *            除了生成下载签名，其他情况updateFlag皆为true
	 * @return 返回base64编码的字符串
	 * @throws AbstractCosException
	 */
	private static String appSignatureBase(Credentials cred, String bucketName, String cosPath, long expired,
			boolean uploadFlag) throws AbstractCosException {
		int appId = cred.getAppId();
		String secretId = cred.getSecretId();
		String secretKey = cred.getSecretKey();
		long now = System.currentTimeMillis() / 1000;
		int rdm = Math.abs(new Random().nextInt());
		String fileId = null;
		if (uploadFlag) {
			fileId = String.format("/%s/%s%s", appId, bucketName, cosPath);
		} else {
			fileId = cosPath;
		}
		fileId = CommonPathUtils.encodeRemotePath(fileId);
		String plainText = String.format("a=%s&k=%s&e=%d&t=%d&r=%d&f=%s&b=%s", appId, secretId, expired, now, rdm,
				fileId, bucketName);

		byte[] hmacDigest;
		try {
			hmacDigest = CommonCodecUtils.HmacSha1(plainText, secretKey);
		} catch (Exception e) {
			throw new UnknownException(e.getMessage());
		}
		byte[] signContent = new byte[hmacDigest.length + plainText.getBytes().length];
		System.arraycopy(hmacDigest, 0, signContent, 0, hmacDigest.length);
		System.arraycopy(plainText.getBytes(), 0, signContent, hmacDigest.length, plainText.getBytes().length);

		return CommonCodecUtils.Base64Encode(signContent);
	}

	/**
	 * 获取多次签名, 一段时间内有效, 针对上传文件，重命名文件, 创建目录, 获取文件目录属性, 拉取目录列表
	 * 
	 * @param bucketName
	 *            bucket名称
	 * @param cosPath
	 *            要签名的cos路径
	 * @param cred
	 *            用户的身份信息, 包括appid, secret_id和secret_key
	 * @param expired
	 *            签名过期时间, UNIX时间戳。如想让签名在30秒后过期, 即可将expired设成当前时间加上30秒
	 * @return base64编码的字符串
	 * @throws AbstractCosException
	 */
	public static String getPeriodEffectiveSign(String bucketName, String cosPath, Credentials cred, long expired)
			throws AbstractCosException {
		return appSignatureBase(cred, bucketName, cosPath, expired, true);
	}

	/**
	 * 获取单次签名, 一次有效，针对删除和更新文件目录
	 * 
	 * @param bucketName
	 *            bucket名称
	 * @param cosPath
	 *            要签名的cos路径
	 * @param cred
	 *            用户的身份信息, 包括appid, secret_id和secret_key
	 * @return base64编码的字符串
	 * @throws AbstractCosException
	 */
	public static String getOneEffectiveSign(String bucketName, String cosPath, Credentials cred)
			throws AbstractCosException {
		return appSignatureBase(cred, bucketName, cosPath, 0, true);
	}

	/**
	 * 下载签名, 用于获取后拼接成下载链接，下载私有bucket的文件
	 * 
	 * @param bucketName
	 *            bucket名称
	 * @param cosPath
	 *            要签名的cos路径
	 * @param cred
	 *            用户的身份信息, 包括appid, secret_id和secret_key
	 * @param expired
	 *            签名过期时间, UNIX时间戳。如想让签名在30秒后过期, 即可将expired设成当前时间加上30秒
	 * @return base64编码的字符串
	 * @throws AbstractCosException
	 */
	public static String getDownLoadSign(String bucketName, String cosPath, Credentials cred, long expired)
			throws AbstractCosException {
		return appSignatureBase(cred, bucketName, cosPath, expired, false);
	}

}
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.common_utils.CommonPathUtils;
import com.qcloud.cos.exception.AbstractCosException;
import com.qcloud.cos.http.AbstractCosHttpClient;
import com.qcloud.cos.http.HttpContentType;
import com.qcloud.cos.http.HttpMethod;
import com.qcloud.cos.http.HttpRequest;
import com.qcloud.cos.http.RequestBodyKey;
import com.qcloud.cos.http.RequestBodyValue;
import com.qcloud.cos.http.RequestHeaderKey;
import com.qcloud.cos.http.RequestHeaderValue;
import com.qcloud.cos.request.AbstractBaseRequest;
import com.qcloud.cos.request.AbstractDelRequest;
import com.qcloud.cos.request.AbstractStatRequest;
import com.qcloud.cos.sign.Credentials;
import com.qcloud.cos.sign.Sign;
package com.qcloud.cos.op;


/**
 * @author chengwu 
 * 封装目录和文件共同的操作
 */
public abstract class BaseOp {
	// 配置信息
	protected ClientConfig config;
	// 鉴权信息
	protected Credentials cred;
	// http请求发送对象
	protected AbstractCosHttpClient httpClient;

	public BaseOp(ClientConfig config, Credentials cred, AbstractCosHttpClient httpClient) {
		super();
		this.config = config;
		this.cred = cred;
		this.httpClient = httpClient;
	}
	
	public void setConfig(ClientConfig config) {
		this.config = config;
	}

	public void setCred(Credentials cred) {
		this.cred = cred;
	}

	public void setHttpClient(AbstractCosHttpClient httpClient) {
		this.httpClient = httpClient;
	}

	/**
	 * 根据APPID, BUCKET, COS_PATH生成经过URL编码的URL
	 * 
	 * @param request
	 *            基本类型的请求
	 * @return URL字符串
	 * @throws AbstractCosException
	 */
	protected String buildUrl(AbstractBaseRequest request) throws AbstractCosException {
		String endPoint = this.config.getCosEndPoint();
		int appId = this.cred.getAppId();
		String bucketName = request.getBucketName();
		String cosPath = request.getCosPath();
		cosPath = CommonPathUtils.encodeRemotePath(cosPath);
		return String.format("%s/%s/%s%s", endPoint, appId, bucketName, cosPath);
	}

	/**
	 * 删除文件或者目录
	 * 
	 * @param request
	 *            删除文件或者目录的请求, 类型为DelFileRequest或者DelFolderRequest
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	protected String delBase(final AbstractDelRequest request) throws AbstractCosException {
		request.check_param();

		String url = buildUrl(request);
		String sign = Sign.getOneEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred);

		HttpRequest httpRequest = new HttpRequest();
		httpRequest.setUrl(url);
		httpRequest.addHeader(RequestHeaderKey.Authorization, sign);
		httpRequest.addHeader(RequestHeaderKey.Content_TYPE, RequestHeaderValue.ContentType.JSON);
		httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent());
		httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.DELETE);