package com.qcloud.cos.op; import java.io.InputStream; import org.json.JSONObject; import com.qcloud.cos.ClientConfig; import com.qcloud.cos.common_utils.CommonCodecUtils; import com.qcloud.cos.common_utils.CommonFileUtils; import com.qcloud.cos.exception.AbstractCosException; import com.qcloud.cos.exception.ParamException; import com.qcloud.cos.exception.UnknownException; 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.http.ResponseBodyKey; import com.qcloud.cos.request.DelFileRequest; import com.qcloud.cos.request.MoveFileRequest; import com.qcloud.cos.request.StatFileRequest; import com.qcloud.cos.request.UpdateFileRequest; import com.qcloud.cos.request.UploadFileRequest; import com.qcloud.cos.request.UploadSliceFileRequest; import com.qcloud.cos.sign.Credentials; import com.qcloud.cos.sign.Sign; /** * @author chengwu * 此类封装了文件操作 */ 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); } }