public class UpdateFileRequest extends AbstractBaseRequest {
    // 用户更新标识，更新bizAttr是0x01, 更新authority0x80, 更新custom_httpheader是0x40
    // 更新多个属性是这些这些值取或
    private int updatFlag = 0;

    // biz_attr属性
    private String bizAttr = "";
    // 权限
    private FileAuthority authority = FileAuthority.INVALID;
    // HTTP Cache-Control属性
    private String cacheControl = "";
    // HTTP Content-Type属性
    private String contentType = "";
    // HTTP Content-Disposition属性
    private String contentDisposition = "";
    // HTTP Content-Language属性
    private String contentLanguage = "";
    // HTTP Content-Encoding属性
    private String contentEncoding = "";
    // 自定义http头
    private Map<String, String> customHeaders = new HashMap<>();
    // 自定义http头, key为x-cos-meta-开头, value为字符串
    private Map<String, String> xCosMetaHeaders = new HashMap<>();

    public UpdateFileRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }

    public int getUpdateFlag() {
        return updatFlag;
    }

    public String getBizAttr() {
        return bizAttr;
    }

    public void setBizAttr(String bizAttr) {
        this.bizAttr = bizAttr;
        this.updatFlag |= 0x01;
    }

    public FileAuthority getAuthority() {
        return authority;
    }

    public void setAuthority(FileAuthority authority) {
        this.authority = authority;
        this.updatFlag |= 0x80;
    }

    public Map<String, String> getCustomHeaders() {
        return customHeaders;
    }

    public void setCacheControl(String cacheControl) {
        this.cacheControl = cacheControl;
        this.updatFlag |= 0x40;
        this.customHeaders.put("Cache-Control", cacheControl);
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
        this.updatFlag |= 0x40;
        this.customHeaders.put("Content-Type", contentType);
    }

    public void setContentDisposition(String contentDisposition) {
        this.contentDisposition = contentDisposition;
        this.updatFlag |= 0x40;
        this.customHeaders.put("Content-Disposition", contentDisposition);
    }

    public void setContentLanguage(String contentLanguage) {
        this.contentLanguage = contentLanguage;
        this.updatFlag |= 0x40;
        this.customHeaders.put("Content-Language", contentLanguage);
    }

    public void setContentEncoding(String contentEncoding) {
        this.contentEncoding = contentEncoding;
        this.updatFlag |= 0x40;
        this.customHeaders.put("Content-Encoding", contentEncoding);
    }

    public void setXCosMeta(String key, String value) {
        this.xCosMetaHeaders.put(key, value);
        this.customHeaders.put(key, value);
        this.updatFlag |= 0x40;
    }

    @Override
    public void check_param() throws ParamException {
        super.check_param();
        CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath());
        CommonParamCheckUtils.AssertLegalUpdateFlag(this.updatFlag);
        CommonParamCheckUtils.AssertNotNull("biz_attr", this.bizAttr);
        CommonParamCheckUtils.AssertNotNull("authority", this.authority);
        CommonParamCheckUtils.AssertNotNull("cacheControl", this.cacheControl);
        CommonParamCheckUtils.AssertNotNull("contentType", this.contentType);
        CommonParamCheckUtils.AssertNotNull("contentDisposition", this.contentDisposition);
        CommonParamCheckUtils.AssertNotNull("contentLanguage", this.contentLanguage);
        CommonParamCheckUtils.AssertNotNull("contentEncoding", this.contentEncoding);
        CommonParamCheckUtils.AssertLegalXCosMeta(this.xCosMetaHeaders);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString());
        sb.append(", biz_attr:").append(getMemberStringValue(bizAttr));
        sb.append(", authority:").append(this.authority);
        sb.append(", cacheControl:").append(getMemberStringValue(this.cacheControl));
        sb.append(", contentType:").append(getMemberStringValue(this.contentType));
        sb.append(", contentDisposition:").append(getMemberStringValue(this.contentDisposition));
        sb.append(", contentLanguage:").append(getMemberStringValue(this.contentLanguage));
        sb.append(", contentEncoding:").append(getMemberStringValue(this.contentEncoding));
        for (String key : this.xCosMetaHeaders.keySet()) {
            sb.append(", x_cos_meta_key:").append(getMemberStringValue(key));
            sb.append(", x_cos_meta_value:")
                    .append(getMemberStringValue(this.xCosMetaHeaders.get(key)));
        }
        sb.append(", authority:");
        if (this.authority == null) {
            sb.append("null");
        } else {
            sb.append(this.authority);
        }
        return sb.toString();
    }
} public class UpdateFolderRequest extends AbstractBaseRequest {

	// biz_attr属性
	private String bizAttr = "";

	public UpdateFolderRequest(String bucketName, String cosPath) {
		super(bucketName, cosPath);
	}

	public String getBizAttr() {
		return bizAttr;
	}

	public void setBizAttr(String bizAttr) {
		this.bizAttr = bizAttr;
	}

	@Override
	public void check_param() throws ParamException {
		super.check_param();
		CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath());
		CommonParamCheckUtils.AssertNotRootCosPath(this.getCosPath());
		CommonParamCheckUtils.AssertNotNull("biz_attr", this.bizAttr);
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(super.toString());
		sb.append(", biz_attr:").append(getMemberStringValue(bizAttr));
		return sb.toString();
	}
} public class UploadFileRequest extends AbstractBaseRequest {
	// 需要上传的路径
	private String localPath;
	// 上传文件的属性信息
	private String bizAttr;

	private InsertOnly insertOnly = InsertOnly.NO_OVER_WRITE;

	public UploadFileRequest(String bucketName, String cosPath, String localPath, String bizAttr) {
		super(bucketName, cosPath);
		this.localPath = localPath;
		this.bizAttr = bizAttr;
	}

	public UploadFileRequest(String bucketName, String cosPath, String localPath) {
		this(bucketName, cosPath, localPath, "");
	}

	public String getBizAttr() {
		return bizAttr;
	}

	public void setBizAttr(String bizAttr) {
		this.bizAttr = bizAttr;
	}

	public String getLocalPath() {
		return localPath;
	}

	public void setLocalPath(String localPath) {
		this.localPath = localPath;
	}


	public InsertOnly getInsertOnly() {
		return insertOnly;
	}

	public void setInsertOnly(InsertOnly insertOnly) {
		this.insertOnly = insertOnly;
	}

	@Override
	public void check_param() throws ParamException {
		super.check_param();
		CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath());
		CommonParamCheckUtils.AssertLegalLocalFilePath(this.localPath);
		CommonParamCheckUtils.AssertNotNull("biz_attr", this.bizAttr);
		CommonParamCheckUtils.AssertNotNull("insertOnly", this.insertOnly);
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(super.toString());
		sb.append(", local_path:").append(getMemberStringValue(this.localPath));
		sb.append(", bizAttr:").append(getMemberStringValue(this.bizAttr));
		sb.append(", insertOnly:");
		if (this.insertOnly == null) {
			sb.append("null");
		} else {
			sb.append(this.insertOnly.ordinal());
		}
		return sb.toString();
	}
} public class UploadSliceFileRequest extends UploadFileRequest {
	// 默认分片大小1MB
	private static final int DEFAULT_SLICE_SIZE = 1024 * 1024;

	private int sliceSize = DEFAULT_SLICE_SIZE;

	public UploadSliceFileRequest(UploadFileRequest request) {
		super(request.getBucketName(), request.getCosPath(), request.getLocalPath(), request.getBizAttr());
		this.setInsertOnly(request.getInsertOnly());
	}

	public UploadSliceFileRequest(String bucketName, String cosPath, String localPath, int sliceSize) {
		super(bucketName, cosPath, localPath);
		this.sliceSize = sliceSize;
	}

	public int getSliceSize() {
		return sliceSize;
	}

	public void setSliceSize(int sliceSize) {
		this.sliceSize = sliceSize;
	}
	
	@Override
	public void check_param() throws ParamException {
		super.check_param();
		CommonParamCheckUtils.AssertLegalSliceSize(this.sliceSize);
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(super.toString());
		sb.append(", sliceSize:").append(this.sliceSize);
		return sb.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 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 class UnknownException extends AbstractCosException {

    private static final long serialVersionUID = 4303770859616883146L;

    public UnknownException(String message) {
        super(CosExceptionType.UNKNOWN_EXCEPTION, message);
    }

} 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 class DefaultCosHttpClient extends AbstractCosHttpClient {

	private static final Logger LOG = LoggerFactory.getLogger(DefaultCosHttpClient.class);

	public DefaultCosHttpClient(ClientConfig config) {
		super(config);
	}

	// 打印HTTP返回码非200的时候的错误信息
	private String getErrorHttpResponseMsg(HttpRequest httpRequest, StatusLine responseStatus) {
		StringBuilder sb = new StringBuilder("HttpRequest:").append(httpRequest.toString());
		sb.append("\nHttpResponse:");
		if (responseStatus.getProtocolVersion() != null) {
			sb.append(" protocol:").append(responseStatus.getProtocolVersion().toString());
		}
		sb.append(", code:").append(responseStatus.getStatusCode());
		if (responseStatus.getReasonPhrase() != null) {
			sb.append(", reasonPhrase:").append(responseStatus.getReasonPhrase());
		}

		LOG.error(sb.toString());
		return sb.toString();
	}

	// 获得异常发生时的返回信息
	private String getExceptionMsg(HttpRequest httpRequest, String exceptionStr) {
		String errMsg = new StringBuilder("HttpRequest:").append(httpRequest.toString()).append("\nException:")
				.append(exceptionStr).toString();
		LOG.error(errMsg);
		return errMsg;
	}

	/**
	 * Get请求函数
	 * 
	 * @param url
	 * @param headers
	 *            额外添加的Http头部
	 * @param params
	 *            GET请求的参数
	 * @return Cos服务器返回的字符串
	 * @throws Exception
	 */
	@Override
	protected String sendGetRequest(HttpRequest httpRequest) throws AbstractCosException {
		String url = httpRequest.getUrl();
		HttpGet httpGet = null;
		String responseStr = "";
		int retry = 0;
		int maxRetryCount = this.config.getMaxFailedRetry();
		while (retry < maxRetryCount) {
			try {
				URIBuilder urlBuilder = new URIBuilder(url);
				for (String paramKey : httpRequest.getParams().keySet()) {
					urlBuilder.addParameter(paramKey, httpRequest.getParams().get(paramKey));
				}
				httpGet = new HttpGet(urlBuilder.build());
			} catch (URISyntaxException e) {
				String errMsg = "Invalid url:" + url;
				LOG.error(errMsg);
				throw new ParamException(errMsg);
			}

			httpGet.setConfig(requestConfig);
			setHeaders(httpGet, httpRequest.getHeaders());

			try {
				HttpResponse httpResponse = httpClient.execute(httpGet);
				;
				int responseStatusCode = httpResponse.getStatusLine().getStatusCode();
				if (responseStatusCode == 200 || responseStatusCode == 400) {
					responseStr = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
					return responseStr;
				} else {
					String errMsg = getErrorHttpResponseMsg(httpRequest, httpResponse.getStatusLine());
					throw new NetworkException(errMsg);
				}
			} catch (ParseException | IOException e) {
				++retry;
				if (retry == maxRetryCount) {
					String errMsg = getExceptionMsg(httpRequest, e.toString());
					throw new ServerException(errMsg);
				}
			} finally {
				httpGet.releaseConnection();
			}
		}
		return responseStr;

	}

	@Override
	protected String sendPostRequest(HttpRequest httpRequest) throws AbstractCosException {
		String url = httpRequest.getUrl();
		String responseStr = "";
		int retry = 0;
		int maxRetryCount = this.config.getMaxFailedRetry();
		while (retry < maxRetryCount) {
			HttpPost httpPost = new HttpPost(url);
			httpPost.setConfig(requestConfig);

			Map<String, String> params = httpRequest.getParams();
			setHeaders(httpPost, httpRequest.getHeaders());

			if (httpRequest.getContentType() == HttpContentType.APPLICATION_JSON) {
				setJsonEntity(httpPost, params);
			} else if (httpRequest.getContentType() == HttpContentType.MULTIPART_FORM_DATA) {
				try {
					setMultiPartEntity(httpPost, params);
				} catch (Exception e) {
					throw new UnknownException(e.toString());
				}
			}

			try {
				HttpResponse httpResponse = httpClient.execute(httpPost);
				int responseStatusCode = httpResponse.getStatusLine().getStatusCode();
				if (responseStatusCode == 200 || responseStatusCode == 400) {
					responseStr = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
					return responseStr;
				} else {
					String errMsg = getErrorHttpResponseMsg(httpRequest, httpResponse.getStatusLine());
					throw new NetworkException(errMsg);
				}
			} catch (ParseException | IOException e) {
				++retry;
				if (retry == maxRetryCount) {
					String errMsg = getExceptionMsg(httpRequest, e.toString());
					throw new ServerException(errMsg);
				}
			} finally {
				httpPost.releaseConnection();
			}
		}
		return responseStr;
	}

	private void setJsonEntity(HttpPost httpPost, Map<String, String> params) {
		ContentType utf8TextPlain = ContentType.create("text/plain", Consts.UTF_8);
		String postJsonStr = new JSONObject(params).toString();
		StringEntity stringEntity = new StringEntity(postJsonStr, utf8TextPlain);
		httpPost.setEntity(stringEntity);
	}

	private void setMultiPartEntity(HttpPost httpPost, Map<String, String> params) throws Exception {
		ContentType utf8TextPlain = ContentType.create("text/plain", Consts.UTF_8);
		MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
		for (String paramKey : params.keySet()) {
			if (paramKey.equals(RequestBodyKey.FILE_CONTENT)) {
				entityBuilder.addBinaryBody(RequestBodyKey.FILE_CONTENT,
						params.get(RequestBodyKey.FILE_CONTENT).getBytes(Charset.forName("ISO-8859-1")));
			} else {
				entityBuilder.addTextBody(paramKey, params.get(paramKey), utf8TextPlain);
			}
		}
		httpPost.setEntity(entityBuilder.build());
	}

	/**
	 * 设置Http头部，同时添加上公共的类型，长连接，COS SDK标识
	 * 
	 * @param message
	 *            HTTP消息
	 * @param headers
	 *            用户额外添加的HTTP头部
	 */
	private void setHeaders(HttpMessage message, Map<String, String> headers) {
		message.setHeader(RequestHeaderKey.ACCEPT, RequestHeaderValue.Accept.ALL);
		message.setHeader(RequestHeaderKey.CONNECTION, RequestHeaderValue.Connection.KEEP_ALIVE);
		message.setHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent());

		if (headers != null) {
			for (String headerKey : headers.keySet()) {
				message.setHeader(headerKey, headers.get(headerKey));
			}
		}
	}

} public class FileOp extends BaseOp {

	public FileOp(ClientConfig config, Credentials cred, AbstractCosHttpClient client) {
		super(config, cred, client);
	}

	/**
	 * 更新文件属性请求
	 * 
	 * @param request
	 *            更新文件属性请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	public String updateFile(final UpdateFileRequest 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.UPDATE);
		int updateFlag = request.getUpdateFlag();
		httpRequest.addParam(RequestBodyKey.UPDATE_FLAG, String.valueOf(updateFlag));
		if ((updateFlag & 0x01) != 0) {
			httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr());
		}
		if ((updateFlag & 0x40) != 0) {
			String customHeaderStr = new JSONObject(request.getCustomHeaders()).toString();
			httpRequest.addParam(RequestBodyKey.CUSTOM_HEADERS, customHeaderStr);
		}
		if ((updateFlag & 0x80) != 0) {
			httpRequest.addParam(RequestBodyKey.AUTHORITY, request.getAuthority().toString());
		}
		httpRequest.setMethod(HttpMethod.POST);
		httpRequest.setContentType(HttpContentType.APPLICATION_JSON);
		return httpClient.sendHttpRequest(httpRequest);
	}

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

		String url = buildUrl(request);
		long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired();
		String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired);

		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.MOVE);
		httpRequest.addParam(RequestBodyKey.DEST_FIELD, request.getDstCosPath());
		httpRequest.addParam(RequestBodyKey.TO_OVER_WRITE, String.valueOf(request.getOverWrite().ordinal()));
		httpRequest.setMethod(HttpMethod.POST);
		httpRequest.setContentType(HttpContentType.APPLICATION_JSON);
		return httpClient.sendHttpRequest(httpRequest);
	}
	
	/**
	 * 获取文件属性请求
	 * 
	 * @param request
	 *            获取文件属性请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	public String statFile(StatFileRequest request) throws AbstractCosException {
		return super.statBase(request);
	}

	/**
	 * 上传文件请求, 对小文件(8MB以下使用单文件上传接口）, 大文件使用分片上传接口
	 * 
	 * @param request
	 *            上传文件请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	public String uploadFile(UploadFileRequest request) throws AbstractCosException {
		request.check_param();

		String localPath = request.getLocalPath();
		long fileSize = 0;
		try {
			fileSize = CommonFileUtils.getFileLength(localPath);
		} catch (Exception e) {
			throw new UnknownException(e.toString());
		}

		long suitSingleFileSize = 8 * 1024 * 1024;
		if (fileSize < suitSingleFileSize) {
			return uploadSingleFile(request);
		} else {
			UploadSliceFileRequest sliceRequest = new UploadSliceFileRequest(request);
			sliceRequest.setInsertOnly(request.getInsertOnly());
			return uploadSliceFile(sliceRequest);
		}
	}

	/**
	 * 上传单文件请求, 不分片
	 * 
	 * @param request
	 *            上传文件请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	public String uploadSingleFile(UploadFileRequest request) throws AbstractCosException {
		request.check_param();

		String localPath = request.getLocalPath();
		long fileSize = 0;
		try {
			fileSize = CommonFileUtils.getFileLength(localPath);
		} catch (Exception e) {
			throw new UnknownException(e.toString());
		}
		// 单文件上传上限不超过20MB
		if (fileSize > 20 * 1024 * 1024) {
			throw new ParamException("file is to big, please use uploadFile interface!");
		}

		String fileContent = "";
		String shaDigest = "";
		try {

			fileContent = CommonFileUtils.getFileContent(localPath);
			shaDigest = CommonCodecUtils.getEntireFileSha1(localPath);
		} catch (Exception e) {
			throw new UnknownException(e.toString());
		}

		String url = buildUrl(request);
		long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired();
		String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired);

		HttpRequest httpRequest = new HttpRequest();
		httpRequest.setUrl(url);
		httpRequest.addHeader(RequestHeaderKey.Authorization, sign);
		httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent());

		httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD);
		httpRequest.addParam(RequestBodyKey.SHA, shaDigest);
		httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr());
		httpRequest.addParam(RequestBodyKey.FILE_CONTENT, fileContent);
		httpRequest.addParam(RequestBodyKey.INSERT_ONLY, String.valueOf(request.getInsertOnly().ordinal()));

		httpRequest.setMethod(HttpMethod.POST);
		httpRequest.setContentType(HttpContentType.MULTIPART_FORM_DATA);

		return httpClient.sendHttpRequest(httpRequest);
	}

	/**
	 * 分片上传文件
	 * 
	 * @param request
	 *            分片上传请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":$mess}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	public String uploadSliceFile(UploadSliceFileRequest request) throws AbstractCosException {
		request.check_param();
		String controlRet = uploadSliceControl(request);
		JSONObject controlRetJson = new JSONObject(controlRet);
		// 如果控制分片已经出错, 则返回
		if (controlRetJson.getInt(ResponseBodyKey.CODE) != 0) {
			return controlRet;
		}
		// 命中秒传
		if (controlRetJson.getJSONObject(ResponseBodyKey.DATA).has(ResponseBodyKey.Data.ACCESS_URL)) {
			return controlRet;
		}
		int sliceSize = controlRetJson.getJSONObject(ResponseBodyKey.DATA).getInt(ResponseBodyKey.Data.SLICE_SIZE);
		long offset = controlRetJson.getJSONObject(ResponseBodyKey.DATA).getLong(ResponseBodyKey.Data.OFFSET);
		String session = controlRetJson.getJSONObject(ResponseBodyKey.DATA).getString(ResponseBodyKey.Data.SESSION);
		String localPath = request.getLocalPath();
		InputStream inputStream = null;
		try {
			long fileSize = CommonFileUtils.getFileLength(localPath);
			inputStream = CommonFileUtils.getFileInputStream(localPath);
			inputStream.skip(offset);
			String uploadDataRet = "";
			while (offset < fileSize) {
				String sliceContent = CommonFileUtils.getFileContent(inputStream, 0, sliceSize);
				uploadDataRet = uploadSliceData(request, sliceContent, session, offset);
				JSONObject dataRetJson = new JSONObject(uploadDataRet);
				if (dataRetJson.getInt(ResponseBodyKey.CODE) != 0) {
					return uploadDataRet;
				} else {
					if (dataRetJson.getJSONObject(ResponseBodyKey.DATA).has(ResponseBodyKey.Data.ACCESS_URL)) {
						return uploadDataRet;
					}
				}
				offset += sliceSize;
			}
			return uploadDataRet;
		} catch (Exception e) {
			throw new UnknownException(e.getMessage());
		} finally {
			CommonFileUtils.closeFileStream(inputStream, localPath);
		}
	}

	/**
	 * 上传控制分片
	 * 
	 * @param request
	 *            分片上传请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "data":{}}, code为0表示成功,
	 *         data为一个JSON结构体,详情请参见WIKI
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	private String uploadSliceControl(UploadSliceFileRequest request) throws AbstractCosException {
		String url = buildUrl(request);
		long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired();
		String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired);

		long fileSize = 0;
		String shaDigest = "";
		try {
			String localPath = request.getLocalPath();
			fileSize = CommonFileUtils.getFileLength(localPath);
			shaDigest = CommonCodecUtils.getEntireFileSha1(localPath);
		} catch (Exception e) {
			throw new UnknownException(e.toString());
		}

		HttpRequest httpRequest = new HttpRequest();
		httpRequest.setUrl(url);
		httpRequest.addHeader(RequestHeaderKey.Authorization, sign);
		httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent());

		httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD_SLICE);
		httpRequest.addParam(RequestBodyKey.SHA, shaDigest);
		httpRequest.addParam(RequestBodyKey.FILE_SIZE, String.valueOf(fileSize));
		httpRequest.addParam(RequestBodyKey.SLICE_SIZE, String.valueOf(request.getSliceSize()));
		httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr());
		httpRequest.addParam(RequestBodyKey.INSERT_ONLY, String.valueOf(request.getInsertOnly().ordinal()));

		return httpClient.sendHttpRequest(httpRequest);
	}

	/**
	 * 上传分片数据
	 * 
	 * @param request
	 *            分片上传请求
	 * @param sliceContent
	 *            分片内容
	 * @param session
	 *            session会话值
	 * @param offset
	 *            分片偏移量
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
	private String uploadSliceData(UploadSliceFileRequest request, String sliceContent, String session, long offset)
			throws AbstractCosException {
		String url = buildUrl(request);
		long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired();
		String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired);

		HttpRequest httpRequest = new HttpRequest();
		httpRequest.setUrl(url);
		httpRequest.addHeader(RequestHeaderKey.Authorization, sign);
		httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent());

		httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD_SLICE);
		httpRequest.addParam(RequestBodyKey.FILE_CONTENT, sliceContent);
		httpRequest.addParam(RequestBodyKey.SESSION, session);
		httpRequest.addParam(RequestBodyKey.OFFSET, String.valueOf(offset));

		return httpClient.sendHttpRequest(httpRequest);
	}
} public class FolderOp extends BaseOp {

    public FolderOp(ClientConfig config, Credentials cred, AbstractCosHttpClient client) {
        super(config, cred, client);
    }

	/**
	 * 更新目录属性请求
	 * 
	 * @param request
	 *            更新目录属性请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
    public String updateFolder(UpdateFolderRequest 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.UPDATE);
		httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr());

		httpRequest.setMethod(HttpMethod.POST);
		httpRequest.setContentType(HttpContentType.APPLICATION_JSON);
		return httpClient.sendHttpRequest(httpRequest);
    }

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

	/**
	 * 获取目录属性请求
	 * 
	 * @param request
	 *            获取目录属性请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
    public String statFolder(StatFolderRequest request) throws AbstractCosException {
    	return super.statBase(request);
    }

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

		String url = buildUrl(request);
		long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired();
		String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired);

		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.CREATE);
		httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr());

		httpRequest.setMethod(HttpMethod.POST);
		httpRequest.setContentType(HttpContentType.APPLICATION_JSON);
		return httpClient.sendHttpRequest(httpRequest);
    }

	/**
	 * 获取目录列表请求
	 * 
	 * @param request
	 *            获取目录列表请求
	 * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功,
	 *         其他为失败, message为success或者失败原因
	 * @throws AbstractCosException
	 *             SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通)
	 */
    public String listFolder(ListFolderRequest request) throws AbstractCosException {

		request.check_param();
		request.setCosPath(request.getCosPath() + request.getPrefix());
		String url = buildUrl(request);
		long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired();
		String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired);

		HttpRequest httpRequest = new HttpRequest();
		httpRequest.setUrl(url);
		httpRequest.addHeader(RequestHeaderKey.Authorization, sign);
		httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent());
		httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.LIST);
		httpRequest.addParam(RequestBodyKey.NUM, String.valueOf(request.getNum()));
		httpRequest.addParam(RequestBodyKey.CONTEXT, request.getContext());
		httpRequest.addParam(RequestBodyKey.ORDER, String.valueOf(request.getOrder().ordinal()));
		httpRequest.addParam(RequestBodyKey.PATTERN, request.getPattern().toString());
		httpRequest.setMethod(HttpMethod.GET);

		return httpClient.sendHttpRequest(httpRequest);
    }

} 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 CreateFolderRequest extends AbstractBaseRequest {
	// 目录属性,默认为空
    private String bizAttr = ""; 

    public CreateFolderRequest(String bucketName, String cosPath) {
        this(bucketName, cosPath, "");
    }
    
    public CreateFolderRequest(String bucketName, String cosPath, String bizAttr) {
        super(bucketName, cosPath);
        this.bizAttr = bizAttr;
    }
    
    public void setBizAttr(String bizAttr) {
    	this.bizAttr = bizAttr;
    }

    public String getBizAttr() {
        return bizAttr;
    }
    
    @Override
    public void check_param() throws ParamException {
    	super.check_param();
    	CommonParamCheckUtils.AssertNotNull("bizAttr", this.bizAttr);
    	CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath());
    	CommonParamCheckUtils.AssertNotRootCosPath(this.getCosPath());
    }
    
    @Override
    public String toString() {
    	StringBuilder sb = new StringBuilder();
    	sb.append(super.toString());
    	sb.append(", bizAttr:").append(getMemberStringValue(bizAttr));
    	return sb.toString();
    }
} public class DelFileRequest extends AbstractDelRequest {

    public DelFileRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }
    
    @Override
    public void check_param() throws ParamException {
    	super.check_param();
    	CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath());
    }
    
    @Override
    public String toString() {
    	return super.toString();
    }
} public class DelFolderRequest extends AbstractDelRequest {

    public DelFolderRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }
    
    @Override
    public void check_param() throws ParamException {
    	super.check_param();
    	CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath());
    	CommonParamCheckUtils.AssertNotRootCosPath(this.getCosPath());
    }
    
    @Override
    public String toString() {
    	return super.toString();
    }
} public class ListFolderRequest extends AbstractBaseRequest {
	
    // 默认获取的最大目录成员数量
    private final int DEFAULT_LIST_NUM = 199;
   
    private int num = DEFAULT_LIST_NUM;
    private String prefix = "";
    private String context = "";
    private ListPattern pattern = ListPattern.BOTH;
    private ListOrder order = ListOrder.POSITIVE;

    public ListFolderRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
    

    public String getPrefix() {
		return prefix;
	}

	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	public String getContext() {
        return context;
    }

    public void setContext(String context) {
        this.context = context;
    }

	public ListPattern getPattern() {
		return pattern;
	}

	public void setPattern(ListPattern pattern) {
		this.pattern = pattern;
	}

	public ListOrder getOrder() {
		return order;
	}

	public void setOrder(ListOrder order) {
		this.order = order;
	}

	@Override
	public void check_param() throws ParamException {
		super.check_param();
		CommonParamCheckUtils.AssertLegalCosFolderPath(getCosPath());
		CommonParamCheckUtils.AssertNotNull("order", this.order);
		CommonParamCheckUtils.AssertNotNull("context", this.context);
		CommonParamCheckUtils.AssertNotNull("prefix", this.prefix);
		CommonParamCheckUtils.AssertNotNull("pattern", this.pattern);
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(super.toString());
		sb.append(", num:").append(this.num);
		sb.append(", order:");
		if (this.order == null) {
			sb.append("null");
		} else {
			sb.append(this.order.ordinal());
		}
		sb.append(", prefix:").append(getMemberStringValue(this.prefix));
		sb.append(", context:").append(getMemberStringValue(this.context));
		sb.append(", pattern:");
		if (this.pattern == null) {
			sb.append("null");
		} else {
			sb.append(this.pattern.toString());
		}
		return sb.toString();
	}

} public class MoveFileRequest extends AbstractBaseRequest {

	private String dstCosPath = "";
	// 移动文件, 如果目的路径已有文件存在, 默认不覆盖
	private OverWrite overWrite = OverWrite.NO_OVER_WRITE;

	public MoveFileRequest(String bucketName, String srcCosPath, String dstCosPath) {
		super(bucketName, srcCosPath);
		this.dstCosPath = dstCosPath;
	}

	public String getDstCosPath() {
		return dstCosPath;
	}

	public void setDstCosPath(String dstCosPath) {
		this.dstCosPath = dstCosPath;
	}

	public OverWrite getOverWrite() {
		return overWrite;
	}

	public void setOverWrite(OverWrite overWrite) {
		this.overWrite = overWrite;
	}

	@Override
	public void check_param() throws ParamException {
		super.check_param();
		CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath());
		CommonParamCheckUtils.AssertLegalCosFilePath(this.dstCosPath);
		CommonParamCheckUtils.AssertNotNull("overWrite", this.overWrite);
	}

	@Override
	public String toString() {
		return super.toString();
	}
} public class StatFileRequest extends AbstractStatRequest {

    public StatFileRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }
    
    @Override
    public void check_param() throws ParamException {
    	super.check_param();
    	CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath());
    }
    
    @Override
    public String toString() {
    	return super.toString();
    }

} public class StatFolderRequest extends AbstractStatRequest {

    public StatFolderRequest(String bucketName, String cosPath) {
        super(bucketName, cosPath);
    }
    
    @Override
    public void check_param() throws ParamException {
    	super.check_param();
    	CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath());
    }

    @Override
    public String toString() {
    	return super.toString();
    }
}
import com.qcloud.cos.request.UpdateFileRequest;
import com.qcloud.cos.request.UpdateFolderRequest;
import com.qcloud.cos.request.UploadFileRequest;
import com.qcloud.cos.request.UploadSliceFileRequest;
import com.qcloud.cos.sign.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.qcloud.cos.exception.AbstractCosException;
import com.qcloud.cos.exception.UnknownException;
import com.qcloud.cos.http.AbstractCosHttpClient;
import com.qcloud.cos.http.DefaultCosHttpClient;
import com.qcloud.cos.op.FileOp;
import com.qcloud.cos.op.FolderOp;
import com.qcloud.cos.request.AbstractBaseRequest;
import com.qcloud.cos.request.CreateFolderRequest;
import com.qcloud.cos.request.DelFileRequest;
import com.qcloud.cos.request.DelFolderRequest;
import com.qcloud.cos.request.ListFolderRequest;
import com.qcloud.cos.request.MoveFileRequest;
import com.qcloud.cos.request.StatFileRequest;
import com.qcloud.cos.request.StatFolderRequest;
package com.qcloud.cos;




/**
 * @author chengwu 封装Cos JAVA SDK暴露给用户的接口函数
 */
public class COSClient implements COS {

	private static final Logger LOG = LoggerFactory.getLogger(COSClient.class);

	private ClientConfig config;
	private Credentials cred;
	private AbstractCosHttpClient client;

	private FileOp fileOp;
	private FolderOp folderOp;

	public COSClient(int appId, String secretId, String secretKey) {
		this(new Credentials(appId, secretId, secretKey));
	}

	public COSClient(Credentials cred) {
		this(new ClientConfig(), cred);
	}

	public void setConfig(ClientConfig config) {
		this.config = config;
		this.fileOp.setConfig(config);
		this.folderOp.setConfig(config);
		this.client.shutdown();
		this.client = new DefaultCosHttpClient(config);
		this.fileOp.setHttpClient(this.client);
		this.folderOp.setHttpClient(this.client);
	}

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

	public COSClient(ClientConfig config, Credentials cred) {
		this.config = config;
		this.cred = cred;
		this.client = new DefaultCosHttpClient(config);
		fileOp = new FileOp(this.config, this.cred, this.client);
		folderOp = new FolderOp(this.config, this.cred, this.client);
	}

	private void recordException(String methodName, AbstractBaseRequest request, String message) {
		LOG.error(methodName + " occur a exception, request:{}, message:{}", request, message);
	}

	@Override
	public String updateFolder(UpdateFolderRequest request) {
		try {
			return folderOp.updateFolder(request);
		} catch (AbstractCosException e) {
			recordException("updateFolder", request, e.toString());
			return e.toString();
		} catch (Exception e) {
			UnknownException e1 = new UnknownException(e.toString());
			recordException("updateFolder", request, e1.toString());
			return e1.toString();
		}
	}

	@Override
	public String updateFile(UpdateFileRequest request) {