├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── qcloud │ └── cos │ ├── COS.java │ ├── COSClient.java │ ├── ClientConfig.java │ ├── ErrorCode.java │ ├── common_utils │ ├── CommonCodecUtils.java │ ├── CommonFileUtils.java │ ├── CommonParamCheckUtils.java │ ├── CommonPathUtils.java │ └── CommonSha1Utils.java │ ├── demo │ └── Demo.java │ ├── exception │ ├── AbstractCosException.java │ ├── CosExceptionType.java │ ├── NetworkException.java │ ├── ParamException.java │ ├── ServerException.java │ └── UnknownException.java │ ├── http │ ├── AbstractCosHttpClient.java │ ├── DefaultCosHttpClient.java │ ├── HttpContentType.java │ ├── HttpMethod.java │ ├── HttpRequest.java │ ├── IdleConnectionMonitorThread.java │ ├── RequestBodyKey.java │ ├── RequestBodyValue.java │ ├── RequestHeaderKey.java │ ├── RequestHeaderValue.java │ └── ResponseBodyKey.java │ ├── meta │ ├── COSObjectInputStream.java │ ├── FileAuthority.java │ ├── FileStat.java │ ├── InsertOnly.java │ ├── OverWrite.java │ ├── SliceFileDataTask.java │ ├── SlicePart.java │ └── UploadSliceFileContext.java │ ├── op │ ├── BaseOp.java │ ├── FileOp.java │ └── FolderOp.java │ ├── request │ ├── AbstractBaseRequest.java │ ├── AbstractDelRequest.java │ ├── AbstractStatRequest.java │ ├── CreateFolderRequest.java │ ├── DelFileRequest.java │ ├── DelFolderRequest.java │ ├── GetFileInputStreamRequest.java │ ├── GetFileLocalRequest.java │ ├── ListFolderRequest.java │ ├── ListPartsRequest.java │ ├── MoveFileRequest.java │ ├── StatFileRequest.java │ ├── StatFolderRequest.java │ ├── UpdateFileRequest.java │ ├── UpdateFolderRequest.java │ ├── UploadFileRequest.java │ └── UploadSliceFileRequest.java │ └── sign │ ├── Credentials.java │ └── Sign.java └── test ├── java └── com │ └── qcloud │ └── cos │ └── common_utils │ └── CommonCodecUtilsTest.java └── resources ├── bigfile.txt ├── empty.txt ├── local_file_1.txt ├── local_file_2.txt └── log4j.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tencentyun-cos-java-sdk-v4 2 | 3 | java sdk for [腾讯云对象存储服务](https://www.qcloud.com/product/cos.html) 4 | 5 | sdk说明请参照[cos java sdk文档](https://www.qcloud.com/doc/product/436/6273) 6 | 7 | ## 已弃用 - 请升级到 cos-java-sdk-v5 8 | SDK 依赖的 JSON API 已弃用,请直接使用基于 XML API 的 [cos-java-sdk-v5](https://github.com/tencentyun/cos-java-sdk-v5),或者参照 [指引](https://cloud.tencent.com/document/product/436/31355) 升级到新版SDK。 9 | 10 | 11 | ## maven坐标 12 | 13 | ```xml 14 | com.qcloud 15 | cos_api 16 | 4.7 17 | ``` 18 | 19 | ### 直接下载源码集成 20 | 从github下载源码装入到您的程序中 21 | 请参考示例Demo.java 22 | 23 | ## 使用范例 24 | 修改Demo.java内的appId, secretId, secretKey等信息为您的配置(可在控制台上查阅相关信息), 然后运行Demo.java。 25 | 26 | 27 | ### 常见问题: 28 | 29 | 1 引入SDK运行后,出现 java.lang.NoSuchMethodError的异常。 30 | 31 | 原因: 一般是发生了JAR包冲突,比如用户的工程中的http的JAR包版本没有A方法,但是SDK依赖的JAR包有A方法。此时运行时加载顺序的问题,加载了用户工程中的http库,运行时便会抛出NoSuchMethodError的异常。 32 | 33 | 解决方法: 将已包含的工程中引起NoSuchMethodError的包的版本和SDK中pom.xml里的对应库的版本改成一致。 34 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.qcloud 6 | cos_api 7 | 4.7 8 | jar 9 | cos-java-sdk 10 | qcloud cos sdk for tencentyun 11 | https://github.com/tencentyun/cos-java-sdk 12 | 13 | 14 | 15 | cos-java-sdk 16 | https://github.com/tencentyun/cos-java-sdk 17 | 18 | 19 | 20 | 21 | 22 | chengwu 23 | chengwu@tencent.com 24 | 25 | 26 | 27 | 28 | scm:git:https://github.com/tencentyun/cos-java-sdk.git 29 | scm:git:https://github.com/tencentyun/cos-java-sdk.git 30 | https://github.com/tencentyun/cos-java-sdk 31 | 32 | 33 | 34 | UTF-8 35 | 1.6 36 | 1.6 37 | 38 | 39 | 40 | 41 | org.apache.httpcomponents 42 | httpclient 43 | 4.5.3 44 | 45 | 46 | org.apache.httpcomponents 47 | httpcore 48 | 4.4.6 49 | 50 | 51 | org.apache.httpcomponents 52 | httpmime 53 | 4.5.2 54 | 55 | 56 | org.json 57 | json 58 | 20240303 59 | 60 | 61 | org.slf4j 62 | slf4j-log4j12 63 | 1.7.21 64 | 65 | 66 | commons-codec 67 | commons-codec 68 | 1.10 69 | 70 | 71 | junit 72 | junit 73 | 4.12 74 | 75 | 76 | 77 | 78 | 79 | oss 80 | cos-java-sdk 81 | 82 | https://oss.sonatype.org/service/local/staging/deploy/maven2 83 | 84 | 85 | 86 | 87 | oss 88 | cos-java-sdk 89 | 90 | https://oss.sonatype.org/content/repositories/snapshots 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | maven-assembly-plugin 99 | 100 | 101 | 102 | com.qcloud.cos.demo.Demo 103 | 104 | 105 | 106 | jar-with-dependencies 107 | 108 | 109 | 110 | 111 | 112 | maven-source-plugin 113 | 2.1 114 | 115 | true 116 | 117 | 118 | 119 | 120 | package 121 | 122 | jar-no-fork 123 | 124 | 125 | 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-javadoc-plugin 131 | 2.9.1 132 | 133 | true 134 | 135 | 136 | 137 | package 138 | 139 | jar 140 | 141 | 142 | 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-gpg-plugin 148 | 1.5 149 | 150 | 151 | sign-artifacts 152 | verify 153 | 154 | sign 155 | 156 | 157 | 158 | 159 | 160 | 161 | org.codehaus.mojo 162 | cobertura-maven-plugin 163 | 2.6 164 | 165 | 166 | html 167 | xml 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | doclint-java8-disable 177 | 178 | [1.8,) 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-javadoc-plugin 186 | 187 | -Xdoclint:none 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/COS.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos; 2 | 3 | import java.io.InputStream; 4 | 5 | import com.qcloud.cos.request.CreateFolderRequest; 6 | import com.qcloud.cos.request.DelFileRequest; 7 | import com.qcloud.cos.request.DelFolderRequest; 8 | import com.qcloud.cos.request.GetFileInputStreamRequest; 9 | import com.qcloud.cos.request.GetFileLocalRequest; 10 | import com.qcloud.cos.request.ListFolderRequest; 11 | import com.qcloud.cos.request.MoveFileRequest; 12 | import com.qcloud.cos.request.StatFileRequest; 13 | import com.qcloud.cos.request.StatFolderRequest; 14 | import com.qcloud.cos.request.UpdateFileRequest; 15 | import com.qcloud.cos.request.UpdateFolderRequest; 16 | import com.qcloud.cos.request.UploadFileRequest; 17 | import com.qcloud.cos.request.UploadSliceFileRequest; 18 | 19 | /** 20 | * @author chengwu COS提供给用户使用的API接口 21 | */ 22 | 23 | public interface COS { 24 | 25 | /** 26 | * 上传文件请求, 对小文件(8MB以下)使用单文件上传接口, 大文件使用分片上传接口, 推荐使用 27 | * 28 | * @param request 上传文件请求 29 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 30 | * message为success或者失败原因 31 | */ 32 | String uploadFile(UploadFileRequest request); 33 | 34 | /** 35 | * 上传单文件请求, 不分片,优先推荐使用uploadFile接口 36 | * 37 | * @param request 上传文件请求 38 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 39 | * message为success或者失败原因 40 | */ 41 | String uploadSingleFile(UploadFileRequest request); 42 | 43 | /** 44 | * 分片上传文件 45 | * 46 | * @param request 分片上传请求 47 | * @return JSON格式的字符串, 格式为{"code":$code, "message":$mess}, code为0表示成功, 其他为失败, 48 | * message为success或者失败原因 49 | */ 50 | String uploadSliceFile(UploadSliceFileRequest request); 51 | 52 | /** 53 | * 获取文件属性 54 | * 55 | * @param request 获取文件属性请求 56 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 57 | * message为success或者失败原因 58 | */ 59 | String statFile(StatFileRequest request); 60 | 61 | /** 62 | * 更新文件属性 63 | * 64 | * @param request 更新文件属性请求 65 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 66 | * message为success或者失败原因 67 | */ 68 | String updateFile(UpdateFileRequest request); 69 | 70 | /** 71 | * 移动文件 72 | * 73 | * @param request 移动文件请求 74 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 75 | * message为success或者失败原因 76 | */ 77 | String moveFile(MoveFileRequest request); 78 | 79 | /** 80 | * 删除文件 81 | * 82 | * @param request 删除文件请求 83 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 84 | * message为success或者失败原因 85 | */ 86 | String delFile(DelFileRequest request); 87 | 88 | 89 | 90 | /** 91 | * 下载文件到本地 92 | * 93 | * @param request 94 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 95 | * message为success或者失败原因 96 | */ 97 | String getFileLocal(GetFileLocalRequest request); 98 | 99 | /** 100 | * 下载文件并得到下载流 101 | * 102 | * @param request 103 | * @return 下载输入流 104 | */ 105 | InputStream getFileInputStream(GetFileInputStreamRequest request) throws Exception; 106 | 107 | /** 108 | * 创建目录 109 | * 110 | * @param request 创建目录请求 111 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 112 | * message为success或者失败原因 113 | */ 114 | String createFolder(CreateFolderRequest request); 115 | 116 | /** 117 | * 更新目录属性 118 | * 119 | * @param request 更新目录属性请求 120 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 121 | * message为success或者失败原因 122 | */ 123 | String updateFolder(UpdateFolderRequest request); 124 | 125 | /** 126 | * 获取目录属性请求 127 | * 128 | * @param request 获取目录属性请求 129 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 130 | * message为success或者失败原因 131 | */ 132 | String statFolder(StatFolderRequest request); 133 | 134 | /** 135 | * 获取目录列表请求 136 | * 137 | * @param request 获取目录列表请求 138 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 139 | * message为success或者失败原因 140 | */ 141 | String listFolder(ListFolderRequest request); 142 | 143 | /** 144 | * 删除目录请求 145 | * 146 | * @param request 删除目录请求 147 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 148 | * message为success或者失败原因 149 | */ 150 | String delFolder(DelFolderRequest request); 151 | 152 | /** 153 | * 关闭COS客户端连接池,释放涉及的资源,释放后,不能再使用COS的接口,必须重新生成一个新对象 154 | */ 155 | void shutdown(); 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/COSClient.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos; 2 | 3 | import com.qcloud.cos.request.UpdateFileRequest; 4 | import com.qcloud.cos.request.UpdateFolderRequest; 5 | import com.qcloud.cos.request.UploadFileRequest; 6 | import com.qcloud.cos.request.UploadSliceFileRequest; 7 | import com.qcloud.cos.sign.Credentials; 8 | 9 | import java.io.InputStream; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import com.qcloud.cos.exception.AbstractCosException; 15 | import com.qcloud.cos.exception.UnknownException; 16 | import com.qcloud.cos.http.AbstractCosHttpClient; 17 | import com.qcloud.cos.http.DefaultCosHttpClient; 18 | import com.qcloud.cos.op.FileOp; 19 | import com.qcloud.cos.op.FolderOp; 20 | import com.qcloud.cos.request.AbstractBaseRequest; 21 | import com.qcloud.cos.request.CreateFolderRequest; 22 | import com.qcloud.cos.request.DelFileRequest; 23 | import com.qcloud.cos.request.DelFolderRequest; 24 | import com.qcloud.cos.request.GetFileInputStreamRequest; 25 | import com.qcloud.cos.request.GetFileLocalRequest; 26 | import com.qcloud.cos.request.ListFolderRequest; 27 | import com.qcloud.cos.request.MoveFileRequest; 28 | import com.qcloud.cos.request.StatFileRequest; 29 | import com.qcloud.cos.request.StatFolderRequest; 30 | 31 | /** 32 | * @author chengwu 封装Cos JAVA SDK暴露给用户的接口函数 33 | */ 34 | public class COSClient implements COS { 35 | 36 | private static final Logger LOG = LoggerFactory.getLogger(COSClient.class); 37 | 38 | private ClientConfig config; 39 | private Credentials cred; 40 | private AbstractCosHttpClient client; 41 | 42 | private FileOp fileOp; 43 | private FolderOp folderOp; 44 | 45 | public COSClient(long appId, String secretId, String secretKey) { 46 | this(new Credentials(appId, secretId, secretKey)); 47 | } 48 | 49 | public COSClient(Credentials cred) { 50 | this(new ClientConfig(), cred); 51 | } 52 | 53 | public void setConfig(ClientConfig config) { 54 | this.config = config; 55 | this.fileOp.setConfig(config); 56 | this.folderOp.setConfig(config); 57 | this.client.shutdown(); 58 | this.client = new DefaultCosHttpClient(config); 59 | this.fileOp.setHttpClient(this.client); 60 | this.folderOp.setHttpClient(this.client); 61 | } 62 | 63 | public void setCred(Credentials cred) { 64 | this.cred = cred; 65 | this.fileOp.setCred(cred); 66 | this.folderOp.setCred(cred); 67 | } 68 | 69 | public COSClient(ClientConfig config, Credentials cred) { 70 | this.config = config; 71 | this.cred = cred; 72 | this.client = new DefaultCosHttpClient(config); 73 | fileOp = new FileOp(this.config, this.cred, this.client); 74 | folderOp = new FolderOp(this.config, this.cred, this.client); 75 | } 76 | 77 | private void recordException(String methodName, AbstractBaseRequest request, String message) { 78 | LOG.warn(methodName + " occur a exception, request:{}, message:{}", request, message); 79 | } 80 | 81 | @Override 82 | public String updateFolder(UpdateFolderRequest request) { 83 | try { 84 | return folderOp.updateFolder(request); 85 | } catch (AbstractCosException e) { 86 | recordException("updateFolder", request, e.toString()); 87 | return e.toString(); 88 | } catch (Exception e) { 89 | UnknownException e1 = new UnknownException(e.toString()); 90 | recordException("updateFolder", request, e1.toString()); 91 | return e1.toString(); 92 | } 93 | } 94 | 95 | @Override 96 | public String updateFile(UpdateFileRequest request) { 97 | try { 98 | return fileOp.updateFile(request); 99 | } catch (AbstractCosException e) { 100 | recordException("updateFile", request, e.toString()); 101 | return e.toString(); 102 | } catch (Exception e) { 103 | UnknownException e1 = new UnknownException(e.toString()); 104 | recordException("updateFile", request, e1.toString()); 105 | return e1.toString(); 106 | } 107 | } 108 | 109 | @Override 110 | public String delFolder(DelFolderRequest request) { 111 | try { 112 | return folderOp.delFolder(request); 113 | } catch (AbstractCosException e) { 114 | recordException("deleteFolder", request, e.toString()); 115 | return e.toString(); 116 | } catch (Exception e) { 117 | UnknownException e1 = new UnknownException(e.toString()); 118 | recordException("deleteFolder", request, e1.toString()); 119 | return e1.toString(); 120 | } 121 | } 122 | 123 | @Override 124 | public String moveFile(MoveFileRequest request) { 125 | try { 126 | return fileOp.moveFile(request); 127 | } catch (AbstractCosException e) { 128 | recordException("moveFile", request, e.toString()); 129 | return e.toString(); 130 | } catch (Exception e) { 131 | UnknownException e1 = new UnknownException(e.toString()); 132 | recordException("moveFile", request, e1.toString()); 133 | return e1.toString(); 134 | } 135 | } 136 | 137 | @Override 138 | public String delFile(DelFileRequest request) { 139 | try { 140 | return fileOp.delFile(request); 141 | } catch (AbstractCosException e) { 142 | recordException("deleteFile", request, e.toString()); 143 | return e.toString(); 144 | } catch (Exception e) { 145 | UnknownException e1 = new UnknownException(e.toString()); 146 | recordException("deleteFile", request, e1.toString()); 147 | return e1.toString(); 148 | } 149 | } 150 | 151 | @Override 152 | public String statFolder(StatFolderRequest request) { 153 | try { 154 | return folderOp.statFolder(request); 155 | } catch (AbstractCosException e) { 156 | recordException("getFolderStat", request, e.toString()); 157 | return e.toString(); 158 | } catch (Exception e) { 159 | UnknownException e1 = new UnknownException(e.toString()); 160 | recordException("getFolderStat", request, e1.toString()); 161 | return e1.toString(); 162 | } 163 | } 164 | 165 | @Override 166 | public String statFile(StatFileRequest request) { 167 | try { 168 | return fileOp.statFile(request); 169 | } catch (AbstractCosException e) { 170 | recordException("getFileStat", request, e.toString()); 171 | return e.toString(); 172 | } catch (Exception e) { 173 | UnknownException e1 = new UnknownException(e.toString()); 174 | recordException("getFileStat", request, e1.toString()); 175 | return e1.toString(); 176 | } 177 | } 178 | 179 | @Override 180 | public String createFolder(CreateFolderRequest request) { 181 | try { 182 | return folderOp.createFolder(request); 183 | } catch (AbstractCosException e) { 184 | recordException("createFolder", request, e.toString()); 185 | return e.toString(); 186 | } catch (Exception e) { 187 | UnknownException e1 = new UnknownException(e.toString()); 188 | recordException("createFolder", request, e1.toString()); 189 | return e1.toString(); 190 | } 191 | } 192 | 193 | @Override 194 | public String listFolder(ListFolderRequest request) { 195 | try { 196 | return folderOp.listFolder(request); 197 | } catch (AbstractCosException e) { 198 | recordException("getFolderList", request, e.toString()); 199 | return e.toString(); 200 | } catch (Exception e) { 201 | UnknownException e1 = new UnknownException(e.toString()); 202 | recordException("getFolderList", request, e1.toString()); 203 | return e1.toString(); 204 | } 205 | } 206 | 207 | @Override 208 | public String uploadFile(UploadFileRequest request) { 209 | try { 210 | return fileOp.uploadFile(request); 211 | } catch (AbstractCosException e) { 212 | recordException("uploadFile", request, e.toString()); 213 | return e.toString(); 214 | } catch (Exception e) { 215 | UnknownException e1 = new UnknownException(e.toString()); 216 | recordException("uploadFile", request, e1.toString()); 217 | return e1.toString(); 218 | } 219 | } 220 | 221 | @Override 222 | public String uploadSingleFile(UploadFileRequest request) { 223 | try { 224 | return fileOp.uploadSingleFile(request); 225 | } catch (AbstractCosException e) { 226 | recordException("uploadSingleFile", request, e.toString()); 227 | return e.toString(); 228 | } catch (Exception e) { 229 | UnknownException e1 = new UnknownException(e.toString()); 230 | recordException("uploadSingleFile", request, e1.toString()); 231 | return e1.toString(); 232 | } 233 | } 234 | 235 | @Override 236 | public String uploadSliceFile(UploadSliceFileRequest request) { 237 | try { 238 | return fileOp.uploadSliceFile(request); 239 | } catch (AbstractCosException e) { 240 | recordException("uploadSliceFile", request, e.toString()); 241 | return e.toString(); 242 | } catch (Exception e) { 243 | UnknownException e1 = new UnknownException(e.toString()); 244 | recordException("uploadSliceFile", request, e1.toString()); 245 | return e1.toString(); 246 | } 247 | } 248 | 249 | @Override 250 | public InputStream getFileInputStream(GetFileInputStreamRequest request) throws Exception { 251 | try { 252 | return fileOp.getFileInputStream(request); 253 | } catch (AbstractCosException e) { 254 | recordException("getFileInputStream", request, e.toString()); 255 | throw new Exception(e.getMessage()); 256 | } catch (Exception e) { 257 | UnknownException e1 = new UnknownException(e.toString()); 258 | recordException("getFileInputStream", request, e1.toString()); 259 | throw new Exception(e.getMessage()); 260 | } 261 | } 262 | 263 | @Override 264 | public String getFileLocal(GetFileLocalRequest request) { 265 | try { 266 | return fileOp.getFileLocal(request); 267 | } catch (AbstractCosException e) { 268 | recordException("getFileLocalRequest", request, e.toString()); 269 | return e.toString(); 270 | } catch (Exception e) { 271 | UnknownException e1 = new UnknownException(e.toString()); 272 | recordException("getFileLocalRequest", request, e1.toString()); 273 | return e1.toString(); 274 | } 275 | } 276 | @Override 277 | public void shutdown() { 278 | this.client.shutdown(); 279 | } 280 | 281 | } 282 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/ClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos; 2 | 3 | public class ClientConfig { 4 | // cos server的上传域名前缀, 用来表明使用的协议 5 | private static final String UPLOAD_COS_ENDPOINT_PREFIX = "http://"; 6 | // cos server的上传域名地址 7 | private static final String UPLOAD_COS_ENDPOINT_DOMAIN = "gz.file.myqcloud.com"; 8 | // cos server的上传域名的后缀 9 | private static final String UPLOAD_COS_ENDPOINT_SUFFIX = "/files/v2"; 10 | // cos server的下载域名的前缀 11 | private static final String DOWN_COS_ENDPOINT_PREFIX = "http://"; 12 | // cos server的下载域名地址 13 | private static final String DOWN_COS_ENDPOINT_DOMAIN = "cosgz.myqcloud.com"; 14 | // 多次签名的默认过期时间,单位秒 15 | private static final int DEFAULT_SIGN_EXPIRED = 300; 16 | // 默认的最大重试次数(发生了socketException时) 17 | private static final int DEFAULT_MAX_RETRIES = 3; 18 | // 默认的获取连接的超时时间 19 | private static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = -1; 20 | // 默认连接超时, 单位ms 21 | private static final int DEFAULT_CONNECTION_TIMEOUT = 30 * 1000; 22 | // 默认的SOCKET读取超时时间, 默认毫秒 23 | private static final int DEFAULT_SOCKET_TIMEOUT = 30 * 1000; 24 | // 默认的维护最大HTTP连接数 25 | private static final int DEFAULT_MAX_CONNECTIONS_COUNT = 1024; 26 | // 默认的user_agent标识 27 | private static final String DEFAULT_USER_AGENT = "cos-java-sdk-v4.7"; 28 | 29 | private String uploadCosEndPointPrefix = UPLOAD_COS_ENDPOINT_PREFIX; 30 | private String uploadCosEndPointDomain = UPLOAD_COS_ENDPOINT_DOMAIN; 31 | private String uploadCosEndPointSuffix = UPLOAD_COS_ENDPOINT_SUFFIX; 32 | private String downCosEndPointPrefix = DOWN_COS_ENDPOINT_PREFIX; 33 | private String downCosEndPointDomain = DOWN_COS_ENDPOINT_DOMAIN; 34 | private int signExpired = DEFAULT_SIGN_EXPIRED; 35 | private int maxFailedRetry = DEFAULT_MAX_RETRIES; 36 | private int connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT; 37 | private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; 38 | private int socketTimeout = DEFAULT_SOCKET_TIMEOUT; 39 | private int maxConnectionsCount = DEFAULT_MAX_CONNECTIONS_COUNT; 40 | private String userAgent = DEFAULT_USER_AGENT; 41 | 42 | // http proxy代理,如果使用http proxy代理,需要设置IP与端口 43 | private String httpProxyIp = null; 44 | private int httpProxyPort = 0; 45 | 46 | 47 | public int getMaxFailedRetry() { 48 | return maxFailedRetry; 49 | } 50 | 51 | public void setMaxFailedRetry(int maxFailedRetry) { 52 | this.maxFailedRetry = maxFailedRetry; 53 | } 54 | 55 | public int getSignExpired() { 56 | return signExpired; 57 | } 58 | 59 | public void setSignExpired(int signExpired) { 60 | this.signExpired = signExpired; 61 | } 62 | 63 | public int getConnectionRequestTimeout() { 64 | return connectionRequestTimeout; 65 | } 66 | 67 | public void setConnectionRequestTimeout(int connectionRequestTimeout) { 68 | this.connectionRequestTimeout = connectionRequestTimeout; 69 | } 70 | 71 | public int getConnectionTimeout() { 72 | return connectionTimeout; 73 | } 74 | 75 | public void setConnectionTimeout(int connectionTimeout) { 76 | this.connectionTimeout = connectionTimeout; 77 | } 78 | 79 | public int getSocketTimeout() { 80 | return socketTimeout; 81 | } 82 | 83 | public void setSocketTimeout(int socketTimeout) { 84 | this.socketTimeout = socketTimeout; 85 | } 86 | 87 | public int getMaxConnectionsCount() { 88 | return maxConnectionsCount; 89 | } 90 | 91 | public void setMaxConnectionsCount(int maxConnectionsCount) { 92 | this.maxConnectionsCount = maxConnectionsCount; 93 | } 94 | 95 | public String getUserAgent() { 96 | return userAgent; 97 | } 98 | 99 | public void setUserAgent(String userAgent) { 100 | this.userAgent = userAgent; 101 | } 102 | 103 | public String getUploadCosEndPointDomain() { 104 | return uploadCosEndPointDomain; 105 | } 106 | 107 | public void setUploadCosEndPointDomain(String cosEndpoint) { 108 | this.uploadCosEndPointDomain = cosEndpoint; 109 | } 110 | 111 | public String getDownCosEndPointDomain() { 112 | return downCosEndPointDomain; 113 | } 114 | 115 | public void setDownCosEndPointDomain(String downCosEndPoint) { 116 | this.downCosEndPointDomain = downCosEndPoint; 117 | } 118 | 119 | public String getUploadCosEndPointPrefix() { 120 | return uploadCosEndPointPrefix; 121 | } 122 | 123 | public void setUploadCosEndPointPrefix(String uploadCosEndPointPrefix) { 124 | this.uploadCosEndPointPrefix = uploadCosEndPointPrefix; 125 | } 126 | 127 | public String getUploadCosEndPointSuffix() { 128 | return uploadCosEndPointSuffix; 129 | } 130 | 131 | public void setUploadCosEndPointSuffix(String uploadCosEndPointSuffix) { 132 | this.uploadCosEndPointSuffix = uploadCosEndPointSuffix; 133 | } 134 | 135 | public String getDownCosEndPointPrefix() { 136 | return downCosEndPointPrefix; 137 | } 138 | 139 | public void setDownCosEndPointPrefix(String downCosEndPointPrefix) { 140 | this.downCosEndPointPrefix = downCosEndPointPrefix; 141 | } 142 | 143 | public void setRegion(String region) { 144 | this.uploadCosEndPointDomain = region + ".file.myqcloud.com"; 145 | this.downCosEndPointDomain = "cos" + region + ".myqcloud.com"; 146 | } 147 | 148 | public String getHttpProxyIp() { 149 | return httpProxyIp; 150 | } 151 | 152 | public void setHttpProxyIp(String httpProxyIp) { 153 | this.httpProxyIp = httpProxyIp; 154 | } 155 | 156 | public int getHttpProxyPort() { 157 | return httpProxyPort; 158 | } 159 | 160 | public void setHttpProxyPort(int httpProxyPort) { 161 | this.httpProxyPort = httpProxyPort; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/ErrorCode.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos; 2 | 3 | /** 4 | * @author chengwu 5 | * cos返回给用户的错误码信息, code为0为成功 6 | * 此处的错误码是SDK包括:用户的参数错误(如路径不符合), 网络错误(无法和cos服务端通信), 服务端故障, 其他未知错误 7 | */ 8 | public class ErrorCode { 9 | public static final int PARAMS_ERROR = -1; // 参数错误 10 | public static final int NETWORK_ERROR = -2; // 网络错误 11 | public static final int SERVER_ERROR = -3; // 服务端故障 12 | public static final int UNKNOWN_ERROR = -4; // 其他未知错误 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/common_utils/CommonCodecUtils.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.common_utils; 2 | 3 | import org.apache.commons.codec.Charsets; 4 | import org.apache.commons.codec.binary.Base64; 5 | import org.apache.commons.codec.digest.DigestUtils; 6 | import org.json.JSONArray; 7 | import org.json.JSONObject; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import com.qcloud.cos.http.RequestBodyKey; 12 | 13 | import java.io.ByteArrayInputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.security.InvalidKeyException; 17 | import java.security.NoSuchAlgorithmException; 18 | 19 | import javax.crypto.Mac; 20 | import javax.crypto.spec.SecretKeySpec; 21 | 22 | /** 23 | * @author chengwu 封装了常用的MD5、SHA1、HmacSha1函数 24 | */ 25 | public class CommonCodecUtils { 26 | 27 | private static final Logger LOG = LoggerFactory.getLogger(CommonCodecUtils.class); 28 | 29 | private static final String HMAC_SHA1 = "HmacSHA1"; 30 | 31 | /** 32 | * 对二进制数据进行BASE64编码 33 | * 34 | * @param binaryData 二进制数据 35 | * @return 编码后的字符串 36 | */ 37 | public static String Base64Encode(byte[] binaryData) { 38 | String encodedstr = new String(Base64.encodeBase64(binaryData, false), Charsets.UTF_8); 39 | return encodedstr; 40 | } 41 | 42 | /** 43 | * 获取buffer内容的sha1 44 | * 45 | * @param contentBuffer 要计算sha1的buffer 46 | * @return 编码后的字符串 47 | * @throws Exception 48 | */ 49 | public static String getBufferSha1(byte[] contentBuffer) throws Exception { 50 | return DigestUtils.sha1Hex(contentBuffer); 51 | } 52 | 53 | /** 54 | * 获取整个文件的SHA1 55 | * 56 | * @param fileInputStream 文件的输入流 57 | * @return 文件对应的SHA1值 58 | * @throws Exception 59 | */ 60 | public static String getEntireFileSha1(String filePath) throws Exception { 61 | InputStream fileInputStream = null; 62 | try { 63 | fileInputStream = CommonFileUtils.getFileInputStream(filePath); 64 | String sha1Digest = DigestUtils.sha1Hex(fileInputStream); 65 | return sha1Digest; 66 | } catch (Exception e) { 67 | String errMsg = "getFileSha1 occur a exception, file:" + filePath + ", exception:" 68 | + e.toString(); 69 | LOG.error(errMsg); 70 | throw new Exception(errMsg); 71 | } finally { 72 | try { 73 | CommonFileUtils.closeFileStream(fileInputStream, filePath); 74 | } catch (Exception e) { 75 | throw e; 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * 获取分片的sha1, 并以JSON数组字符串的形式返回,每一个成员都是分片的sha信息 82 | * 除最后一片外,每一片的sha信息都是中间状态,即sha算法update后的五个常量值的十六进制字符串 因此最后一片的sha值和全文sha是一样的 83 | * 84 | * @param localPath 本地文件路径 85 | * @param sliceSize 分片大小 86 | * @param entireSha1Builder 存储全文sha的对象 87 | * @return 返回分片sha的JSON格式的字符串 88 | * @throws Exception 89 | */ 90 | public static String getSlicePartSha1(String localPath, int sliceSize, 91 | StringBuilder entireSha1Builder) throws Exception { 92 | // 超过1M的按照1M来计算sha 93 | if (sliceSize > 1024 * 1024) { 94 | sliceSize = 1024 * 1024; 95 | } 96 | 97 | JSONArray jsonArray = new JSONArray(); 98 | InputStream fileInput = null; 99 | try { 100 | CommonSha1Utils sha1Utils = new CommonSha1Utils(); 101 | sha1Utils.init(); 102 | 103 | fileInput = CommonFileUtils.getFileInputStream(localPath); 104 | long fileLength = CommonFileUtils.getFileLength(localPath); 105 | int sliceCount = new Long((fileLength + (sliceSize - 1)) / sliceSize).intValue(); 106 | 107 | final int BUFFER_LEN = 1024; 108 | byte[] contentBuf = null; 109 | 110 | // 先求第一片到倒数第二片的sha信息,这些片的大小都是sliceSize 111 | for (int sliceIndex = 0; sliceIndex < sliceCount - 1; ++sliceIndex) { 112 | int totalCount = 0; 113 | long sliceOffset = sliceIndex; 114 | sliceOffset *= sliceSize; 115 | 116 | while (totalCount < sliceSize) { 117 | int maxRead = sliceSize - totalCount; 118 | if (maxRead > BUFFER_LEN) { 119 | maxRead = BUFFER_LEN; 120 | } 121 | contentBuf = new byte[maxRead]; 122 | fileInput.read(contentBuf, 0, maxRead); 123 | sha1Utils.update(contentBuf); 124 | totalCount += maxRead; 125 | } 126 | 127 | JSONObject sliceJson = new JSONObject(); 128 | sliceJson.put(RequestBodyKey.UploadParts.OFFSET, sliceOffset); 129 | sliceJson.put(RequestBodyKey.UploadParts.DATA_LEN, totalCount); 130 | sliceJson.put(RequestBodyKey.UploadParts.DATA_SHA, sha1Utils.dumpTempState()); 131 | jsonArray.put(sliceIndex, sliceJson); 132 | } 133 | 134 | // 求最后一片的sha信息 135 | long sliceOffset = (sliceCount - 1) * (long)sliceSize; 136 | int leftSlice = new Long(fileLength - sliceOffset).intValue(); 137 | int totalCount = 0; 138 | while (totalCount < leftSlice) { 139 | int maxRead = leftSlice - totalCount; 140 | if (maxRead > BUFFER_LEN) { 141 | maxRead = BUFFER_LEN; 142 | } 143 | contentBuf = new byte[maxRead]; 144 | fileInput.read(contentBuf, 0, maxRead); 145 | sha1Utils.update(contentBuf); 146 | totalCount += maxRead; 147 | } 148 | sha1Utils.finish(); 149 | 150 | entireSha1Builder.append(sha1Utils.digout()); 151 | 152 | JSONObject sliceJson = new JSONObject(); 153 | sliceJson.put(RequestBodyKey.UploadParts.OFFSET, sliceOffset); 154 | sliceJson.put(RequestBodyKey.UploadParts.DATA_LEN, totalCount); 155 | sliceJson.put(RequestBodyKey.UploadParts.DATA_SHA, sha1Utils.digout()); 156 | jsonArray.put(sliceCount - 1, sliceJson); 157 | 158 | } catch (Exception e) { 159 | LOG.error("getSlicePartSha1 occur a error, filePath:{}, sliceSize:{}, exception:{}", 160 | localPath, sliceSize, e.toString()); 161 | throw e; 162 | } finally { 163 | CommonFileUtils.closeFileStream(fileInput, localPath); 164 | } 165 | return jsonArray.toString(); 166 | } 167 | 168 | public static String getSlicePartSha1(byte[] contentBuffer, int sliceSize, 169 | StringBuilder entireSha1Builder) throws Exception { 170 | // 超过1M的按照1M来计算sha 171 | if (sliceSize > 1024 * 1024) { 172 | sliceSize = 1024 * 1024; 173 | } 174 | 175 | JSONArray jsonArray = new JSONArray(); 176 | InputStream fileInput = null; 177 | try { 178 | CommonSha1Utils sha1Utils = new CommonSha1Utils(); 179 | sha1Utils.init(); 180 | 181 | fileInput = new ByteArrayInputStream(contentBuffer); 182 | long fileLength = contentBuffer.length; 183 | int sliceCount = new Long((fileLength + (sliceSize - 1)) / sliceSize).intValue(); 184 | 185 | final int BUFFER_LEN = 1024; 186 | byte[] contentBuf = null; 187 | 188 | // 先求第一片到倒数第二片的sha信息,这些片的大小都是sliceSize 189 | for (int sliceIndex = 0; sliceIndex < sliceCount - 1; ++sliceIndex) { 190 | int totalCount = 0; 191 | long sliceOffset = sliceIndex * (long)sliceSize; 192 | 193 | while (totalCount < sliceSize) { 194 | int maxRead = sliceSize - totalCount; 195 | if (maxRead > BUFFER_LEN) { 196 | maxRead = BUFFER_LEN; 197 | } 198 | contentBuf = new byte[maxRead]; 199 | fileInput.read(contentBuf, 0, maxRead); 200 | sha1Utils.update(contentBuf); 201 | totalCount += maxRead; 202 | } 203 | 204 | JSONObject sliceJson = new JSONObject(); 205 | sliceJson.put(RequestBodyKey.UploadParts.OFFSET, sliceOffset); 206 | sliceJson.put(RequestBodyKey.UploadParts.DATA_LEN, totalCount); 207 | sliceJson.put(RequestBodyKey.UploadParts.DATA_SHA, sha1Utils.dumpTempState()); 208 | jsonArray.put(sliceIndex, sliceJson); 209 | } 210 | 211 | // 求最后一片的sha信息 212 | long sliceOffset = (sliceCount - 1) * (long)sliceSize; 213 | int leftSlice = new Long(fileLength - sliceOffset).intValue(); 214 | int totalCount = 0; 215 | while (totalCount < leftSlice) { 216 | int maxRead = leftSlice - totalCount; 217 | if (maxRead > BUFFER_LEN) { 218 | maxRead = BUFFER_LEN; 219 | } 220 | contentBuf = new byte[maxRead]; 221 | fileInput.read(contentBuf, 0, maxRead); 222 | sha1Utils.update(contentBuf); 223 | totalCount += maxRead; 224 | } 225 | sha1Utils.finish(); 226 | 227 | entireSha1Builder.append(sha1Utils.digout()); 228 | 229 | JSONObject sliceJson = new JSONObject(); 230 | sliceJson.put(RequestBodyKey.UploadParts.OFFSET, sliceOffset); 231 | sliceJson.put(RequestBodyKey.UploadParts.DATA_LEN, totalCount); 232 | sliceJson.put(RequestBodyKey.UploadParts.DATA_SHA, sha1Utils.digout()); 233 | jsonArray.put(sliceCount - 1, sliceJson); 234 | 235 | } catch (Exception e) { 236 | LOG.error("getSlicePartSha1 from buffer occur a error, sliceSize:{}, exception:{}", 237 | sliceSize, e.toString()); 238 | throw e; 239 | } finally { 240 | if (fileInput != null) { 241 | try { 242 | fileInput.close(); 243 | } catch (IOException e) { 244 | } 245 | } 246 | } 247 | 248 | return jsonArray.toString(); 249 | } 250 | 251 | /** 252 | * 计算数据的Hmac值 253 | * 254 | * @param binaryData 二进制数据 255 | * @param key 秘钥 256 | * @return 加密后的hmacsha1值 257 | */ 258 | public static byte[] HmacSha1(byte[] binaryData, String key) throws Exception { 259 | try { 260 | Mac mac = Mac.getInstance(HMAC_SHA1); 261 | SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1); 262 | mac.init(secretKey); 263 | byte[] HmacSha1Digest = mac.doFinal(binaryData); 264 | return HmacSha1Digest; 265 | 266 | } catch (NoSuchAlgorithmException e) { 267 | LOG.error("mac not find algorithm {}", HMAC_SHA1); 268 | throw e; 269 | } catch (InvalidKeyException e) { 270 | LOG.error("mac init key {} occur a error {}", key, e.toString()); 271 | throw e; 272 | } catch (IllegalStateException e) { 273 | LOG.error("mac.doFinal occur a error {}", e.toString()); 274 | throw e; 275 | } 276 | } 277 | 278 | /** 279 | * 计算数据的Hmac值 280 | * 281 | * @param plainText 文本数据 282 | * @param key 秘钥 283 | * @return 加密后的hmacsha1值 284 | */ 285 | public static byte[] HmacSha1(String plainText, String key) throws Exception { 286 | return HmacSha1(plainText.getBytes(), key); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/common_utils/CommonFileUtils.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.common_utils; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.nio.charset.Charset; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @author chengwu 15 | * 封装了一些常用的文件操作函数 16 | */ 17 | public class CommonFileUtils { 18 | 19 | private static Logger LOG = LoggerFactory.getLogger(CommonFileUtils.class); 20 | 21 | /** 22 | * 判断指定路径的文件是否有效, 即文件存在,且可读 23 | * @param filePath 24 | * @return 有效返回true, 否则返回false 25 | */ 26 | public static boolean isLegalFile(String filePath) { 27 | File file = new File(filePath); 28 | if (!file.exists() || file.isDirectory() || !file.canRead()) { 29 | return false; 30 | } 31 | return true; 32 | } 33 | 34 | /** 35 | * 获取文件长度,单位为字节 36 | * 37 | * @param filePath 文件的本地路径 38 | * @return 文件长度,单位为字节 39 | * @throws Exception 文件不存在或者是一个目录,则抛出异常 40 | */ 41 | public static long getFileLength(String filePath) throws Exception { 42 | File file = new File(filePath); 43 | return file.length(); 44 | } 45 | 46 | /** 47 | * 打开对应的文件,并返回文件输入流 48 | * 49 | * @param filePath 文件路径 50 | * @return 文件输入流 51 | * @throws FileNotFoundException 如果文件不存在,则抛出异常 52 | */ 53 | public static FileInputStream getFileInputStream(String filePath) throws Exception { 54 | FileInputStream localFileInputStream = new FileInputStream(filePath); 55 | return localFileInputStream; 56 | } 57 | 58 | /** 59 | * 关闭对应的文件流 60 | * 61 | * @param inputStream 待关闭的文件流 62 | * @param filePath 对应的文件名 63 | * @throws IOException 关闭时发生IO异常,则抛出 64 | */ 65 | public static void closeFileStream(InputStream inputStream, String filePath){ 66 | try { 67 | if (inputStream != null) { 68 | inputStream.close(); 69 | } 70 | } catch (IOException e) { 71 | LOG.error("close file {} occur an IOExcpetion {}", filePath, e); 72 | } 73 | } 74 | 75 | /** 76 | * 获取小文件的全部内容 77 | * 78 | * @param filePath 79 | * @return 80 | * @throws Exception 81 | */ 82 | public static String getFileContent(String filePath) throws Exception { 83 | int fileLength = ((Long) getFileLength(filePath)).intValue(); 84 | return getFileContent(filePath, 0, fileLength); 85 | } 86 | 87 | /** 88 | * 获取文件指定块的内容 89 | * 90 | * @param filePath 文件路径 91 | * @param offset 偏移量,即从哪里开始读取,单位为字节 92 | * @param length 读取的长度,单位为字节 93 | * @return 返回读取的内容,实际读取的长度小于等于length 94 | * @throws Exception 95 | */ 96 | public static String getFileContent(String filePath, long offset, int length) throws Exception { 97 | FileInputStream fileInputStream = null; 98 | try { 99 | fileInputStream = getFileInputStream(filePath); 100 | return getFileContent(fileInputStream, offset, length); 101 | } finally { 102 | closeFileStream(fileInputStream, filePath); 103 | } 104 | } 105 | 106 | /** 107 | * 读取指定流从某处开始的内容,此函数有一定的风险,如果流对应的内容过大,则会造成OOM 108 | * 109 | * @param inputStream 110 | * @param offset 读取的开始偏移 111 | * @param length 读取的长度 112 | * @return 读取的内容 113 | * @throws Exception 114 | */ 115 | public static String getFileContent(InputStream inputStream, long offset, int length) 116 | throws Exception { 117 | byte[] fileContent = getFileContentByte(inputStream, offset, length); 118 | return new String(fileContent, Charset.forName("ISO-8859-1")); 119 | } 120 | 121 | public static byte[] getFileContentByte(InputStream inputStream, long offset, int length) 122 | throws Exception { 123 | if (offset < 0 || length < 0) { 124 | throw new Exception("getFileContent param error"); 125 | } 126 | 127 | byte[] fileContent = null; 128 | byte[] tempBuf = new byte[length]; 129 | 130 | inputStream.skip(offset); 131 | int readLen = inputStream.read(tempBuf); 132 | if (readLen < 0) { 133 | fileContent = new byte[0]; 134 | return fileContent; 135 | } 136 | if (readLen < length) { 137 | fileContent = new byte[readLen]; 138 | System.arraycopy(tempBuf, 0, fileContent, 0, readLen); 139 | } else { 140 | fileContent = tempBuf; 141 | } 142 | return fileContent; 143 | } 144 | 145 | /** 146 | * 删除文件 147 | * @param filePath 文件路径 148 | */ 149 | public static void remove(String filePath) { 150 | File file = new File(filePath); 151 | if (file.exists() && file.isFile()) { 152 | file.delete(); 153 | } 154 | } 155 | 156 | /** 157 | * 获取文件上次的修改时间 158 | * 159 | * @param filePath 文件的本地路径 160 | * @return 文件长度,单位为字节 161 | * @throws Exception 文件不存在或者是一个目录,则抛出异常 162 | */ 163 | public static long getFileLastModified(String filePath) throws Exception { 164 | if (!isLegalFile(filePath)) { 165 | String errorMsg = filePath + " is not file or not exist or can't be read!"; 166 | LOG.error(errorMsg); 167 | throw new Exception(errorMsg); 168 | } 169 | File file = new File(filePath); 170 | return file.lastModified(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/common_utils/CommonParamCheckUtils.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.common_utils; 2 | 3 | import java.util.Map; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | import com.qcloud.cos.exception.ParamException; 8 | 9 | /** 10 | * @author chengwu 封装一些参数检查的类,如果检查未通过抛出参数异常 11 | */ 12 | public class CommonParamCheckUtils { 13 | 14 | /** 15 | * 判断参数是否为NULL,如果为NULL,抛出参数异常 16 | * 17 | * @param objName 18 | * 参数名 19 | * @param obj 20 | * 参数对象 21 | * @throws ParamException 22 | */ 23 | public static void AssertNotNull(String objName, Object obj) throws ParamException { 24 | if (obj == null) { 25 | throw new ParamException(objName + " is null, please check!"); 26 | } 27 | } 28 | 29 | /** 30 | * 判断文件是否适合使用整文件上传,使用范围0 ~ 10MB, 大文件应该用分片上传 31 | * 32 | * @param localFilePath 33 | * 本地文件路径 34 | * @throws ParamException 35 | */ 36 | public static void AssertUploadEntireFileInRange(String localFilePath) throws ParamException { 37 | long fileSize = 0; 38 | try { 39 | fileSize = CommonFileUtils.getFileLength(localFilePath); 40 | } catch (Exception e) { 41 | throw new ParamException(localFilePath + " is not effective file!"); 42 | } 43 | 44 | long maxFileSize = 10 * 1024 * 1024; 45 | if (fileSize > maxFileSize) { 46 | throw new ParamException(localFilePath + " is too large, please use uploadSliceFile interface!"); 47 | } 48 | } 49 | 50 | /** 51 | * 判断分片尺寸是否在规定的范围内,抛出参数异常,目前的有效值为64KB ~ 10MB 52 | * 53 | * @param sliceSize 54 | * 分片大小, 单位Byte 55 | * @throws ParamException 56 | */ 57 | public static void AssertSliceInRange(int sliceSize) throws ParamException { 58 | int maxSliceSize = 10 * 1024 * 1024; // 10MB 59 | int minSliceSize = 64 * 1024; // 64KB 60 | if (sliceSize > maxSliceSize || sliceSize < minSliceSize) { 61 | throw new ParamException("sliceSize legal value is [64KB, 100MB]"); 62 | } 63 | } 64 | 65 | private static void AssertNotContainIllegalLetter(String cosPath) throws ParamException { 66 | String[] illegalLetters = {"?", "*", ":", "|", "\\", "<", ">", "\""}; 67 | for (String illegalLetter : illegalLetters) { 68 | if (cosPath.contains(illegalLetter)) { 69 | throw new ParamException("cosFilePath contail illeagl letter " + illegalLetter); 70 | } 71 | } 72 | String pattern = "/(\\s*)/"; 73 | Pattern r = Pattern.compile(pattern); 74 | Matcher m = r.matcher(cosPath); 75 | if (m.find()) { 76 | throw new ParamException("cosFilePath contail illeagl letter / /"); 77 | } 78 | } 79 | 80 | /** 81 | * 判断用户指定的cos目录路径是否合法有效, 即必须以/结尾, 同时不包含非法字符 82 | * 83 | * @param cosFolderPath 84 | * cos目录路径 85 | * @throws ParamException 86 | */ 87 | public static void AssertLegalCosFolderPath(String cosFolderPath) throws ParamException { 88 | if (cosFolderPath == null || !cosFolderPath.startsWith("/") || !cosFolderPath.endsWith("/")) { 89 | throw new ParamException(cosFolderPath + " is not cos folder path! Tips: make sure ends with /"); 90 | } 91 | AssertNotContainIllegalLetter(cosFolderPath); 92 | } 93 | 94 | /** 95 | * 判断用户指定的cos文件路径是否合法有效, 即不以/结尾 96 | * 97 | * @param cosFilePath 98 | * cos文件路径 99 | * @throws ParamException 100 | */ 101 | public static void AssertLegalCosFilePath(String cosFilePath) throws ParamException { 102 | if (cosFilePath == null || !cosFilePath.startsWith("/") || cosFilePath.endsWith("/")) { 103 | throw new ParamException(cosFilePath + " is not cos file path! Tips: make sure not ends with /"); 104 | } 105 | } 106 | 107 | /** 108 | * 判断cos目录是否是根路径,即路径为/ 109 | * 110 | * @param cosCosFolderPath 111 | * cos目录路径 112 | * @throws ParamException 113 | */ 114 | public static void AssertNotRootCosPath(String cosCosFolderPath) throws ParamException { 115 | if (cosCosFolderPath == null || cosCosFolderPath.equals("/")) { 116 | throw new ParamException( 117 | "bucket operation is only allowed by web console! please visit http://console.qcloud.com/cos!"); 118 | } 119 | } 120 | 121 | /** 122 | * 判断用户指定的本地文件路径是否合法有效,即文件存在且可读 123 | * 124 | * @param localFilePath 125 | * 本地文件路径 126 | * @throws ParamException 127 | */ 128 | public static void AssertLegalLocalFilePath(String localFilePath) throws ParamException { 129 | if (localFilePath == null || !CommonFileUtils.isLegalFile(localFilePath)) { 130 | throw new ParamException(localFilePath + " is not file or not exist or can't be read!"); 131 | } 132 | } 133 | 134 | public static void AssertLegalXCosMeta(Map xCosMetaMap) throws ParamException { 135 | for (String x_cos_meta_key : xCosMetaMap.keySet()) { 136 | AssertNotNull("x_cos_meta_key", x_cos_meta_key); 137 | if (!x_cos_meta_key.startsWith("x-cos-meta-")) { 138 | throw new ParamException("x-cos-meta name must starts with x-cos-meta-"); 139 | } 140 | String x_cos_meta_value = xCosMetaMap.get(x_cos_meta_key); 141 | AssertNotNull("x_cos_meta_value", x_cos_meta_value); 142 | if (x_cos_meta_value.isEmpty()) { 143 | throw new ParamException("x-cos-meta value can't be empty!"); 144 | } 145 | } 146 | 147 | } 148 | 149 | public static void AssertLegalUpdateFlag(int updateFlag) throws ParamException { 150 | if (updateFlag == 0) { 151 | throw new ParamException("please update at least one attribute!"); 152 | } 153 | } 154 | 155 | public static void AssertLegalSliceSize(int sliceSize) throws ParamException { 156 | if (sliceSize != 512 * 1024 && sliceSize != 1024 * 1024 && sliceSize != 2 * 1024 * 1024 157 | && sliceSize != 3 * 1024 * 1024) { 158 | throw new ParamException("valid slice is 512KB, 1MB, 2MB, 3MB"); 159 | } else { 160 | 161 | } 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/common_utils/CommonPathUtils.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.common_utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLEncoder; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.qcloud.cos.exception.AbstractCosException; 10 | import com.qcloud.cos.exception.UnknownException; 11 | 12 | public class CommonPathUtils { 13 | private static final Logger LOG = LoggerFactory.getLogger(CommonPathUtils.class); 14 | private static final String PATH_DELIMITER = "/"; 15 | 16 | public static String encodeRemotePath(String urlPath) throws AbstractCosException { 17 | StringBuilder pathBuilder = new StringBuilder(); 18 | String[] pathSegmentsArr = urlPath.split(PATH_DELIMITER); 19 | 20 | for (String pathSegment : pathSegmentsArr) { 21 | if (!pathSegment.isEmpty()) { 22 | try { 23 | pathBuilder.append(PATH_DELIMITER).append(URLEncoder.encode(pathSegment, "UTF-8").replace("+", "%20")); 24 | } catch (UnsupportedEncodingException e) { 25 | String errMsg = "Unsupported ecnode exception:" + e.toString(); 26 | LOG.error(errMsg); 27 | throw new UnknownException(errMsg); 28 | } 29 | } 30 | } 31 | if (urlPath.endsWith(PATH_DELIMITER)) { 32 | pathBuilder.append(PATH_DELIMITER); 33 | } 34 | return pathBuilder.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/common_utils/CommonSha1Utils.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.common_utils; 2 | 3 | /** 4 | * 求sha1, 未使用JAVA自带类,是为了获取分片的中间状态(state数组) 5 | * @author chengwu 6 | * 7 | */ 8 | public final class CommonSha1Utils { 9 | private int state[] = new int[5]; 10 | private long count; 11 | public byte[] digestBits; 12 | public boolean digestValid; 13 | 14 | public CommonSha1Utils() { 15 | state = new int[5]; 16 | count = 0; 17 | if (block == null) 18 | block = new int[16]; 19 | digestBits = new byte[20]; 20 | digestValid = false; 21 | } 22 | 23 | /** 24 | * Add specific bytes to the digest. 25 | */ 26 | public synchronized void update(byte input[], int offset, int len) { 27 | for (int i = 0; i < len; i++) { 28 | update(input[i + offset]); 29 | } 30 | } 31 | 32 | /** 33 | * Add an array of bytes to the digest. 34 | */ 35 | public synchronized void update(byte input[]) { 36 | update(input, 0, input.length); 37 | } 38 | 39 | /** 40 | * Treat the string as a sequence of ISO-Latin1 (8 bit) characters. 41 | */ 42 | public void updateASCII(String input) { 43 | int i, len; 44 | byte x; 45 | 46 | len = input.length(); 47 | for (i = 0; i < len; i++) { 48 | x = (byte) (input.charAt(i) & 0xff); 49 | update(x); 50 | } 51 | } 52 | 53 | /* 54 | * The following array forms the basis for the transform buffer. Update puts bytes into this 55 | * buffer and then transform adds it into the state of the digest. 56 | */ 57 | private int block[] = new int[16]; 58 | private int blockIndex; 59 | 60 | /* 61 | * These functions are taken out of #defines in Steve's code. Java doesn't have a preprocessor 62 | * so the first step is to just promote them to real methods. Later we can optimize them out 63 | * into inline code, note that by making them final some compilers will inline them when given 64 | * the -O flag. 65 | */ 66 | final int rol(int value, int bits) { 67 | int q = (value << bits) | (value >>> (32 - bits)); 68 | return q; 69 | } 70 | 71 | final int blk0(int i) { 72 | block[i] = (rol(block[i], 24) & 0xFF00FF00) | (rol(block[i], 8) & 0x00FF00FF); 73 | return block[i]; 74 | } 75 | 76 | final int blk(int i) { 77 | block[i & 15] = rol(block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] 78 | ^ block[i & 15], 1); 79 | return (block[i & 15]); 80 | } 81 | 82 | final void R0(int data[], int v, int w, int x, int y, int z, int i) { 83 | data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk0(i) + 0x5A827999 84 | + rol(data[v], 5); 85 | data[w] = rol(data[w], 30); 86 | } 87 | 88 | final void R1(int data[], int v, int w, int x, int y, int z, int i) { 89 | data[z] += 90 | ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk(i) + 0x5A827999 + rol(data[v], 5); 91 | data[w] = rol(data[w], 30); 92 | } 93 | 94 | final void R2(int data[], int v, int w, int x, int y, int z, int i) { 95 | data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0x6ED9EBA1 + rol(data[v], 5); 96 | data[w] = rol(data[w], 30); 97 | } 98 | 99 | final void R3(int data[], int v, int w, int x, int y, int z, int i) { 100 | data[z] += (((data[w] | data[x]) & data[y]) | (data[w] & data[x])) + blk(i) + 0x8F1BBCDC 101 | + rol(data[v], 5); 102 | data[w] = rol(data[w], 30); 103 | } 104 | 105 | final void R4(int data[], int v, int w, int x, int y, int z, int i) { 106 | data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0xCA62C1D6 + rol(data[v], 5); 107 | data[w] = rol(data[w], 30); 108 | } 109 | 110 | int dd[] = new int[5]; 111 | 112 | /** 113 | * Hash a single 512-bit block. This is the core of the algorithm. 114 | * 115 | * Note that working with arrays is very inefficent in Java as it does a class cast check each 116 | * time you store into the array. 117 | * 118 | */ 119 | 120 | void transform() { 121 | 122 | /* Copy context->state[] to working vars */ 123 | dd[0] = state[0]; 124 | dd[1] = state[1]; 125 | dd[2] = state[2]; 126 | dd[3] = state[3]; 127 | dd[4] = state[4]; 128 | /* 4 rounds of 20 operations each. Loop unrolled. */ 129 | R0(dd, 0, 1, 2, 3, 4, 0); 130 | R0(dd, 4, 0, 1, 2, 3, 1); 131 | R0(dd, 3, 4, 0, 1, 2, 2); 132 | R0(dd, 2, 3, 4, 0, 1, 3); 133 | R0(dd, 1, 2, 3, 4, 0, 4); 134 | R0(dd, 0, 1, 2, 3, 4, 5); 135 | R0(dd, 4, 0, 1, 2, 3, 6); 136 | R0(dd, 3, 4, 0, 1, 2, 7); 137 | R0(dd, 2, 3, 4, 0, 1, 8); 138 | R0(dd, 1, 2, 3, 4, 0, 9); 139 | R0(dd, 0, 1, 2, 3, 4, 10); 140 | R0(dd, 4, 0, 1, 2, 3, 11); 141 | R0(dd, 3, 4, 0, 1, 2, 12); 142 | R0(dd, 2, 3, 4, 0, 1, 13); 143 | R0(dd, 1, 2, 3, 4, 0, 14); 144 | R0(dd, 0, 1, 2, 3, 4, 15); 145 | R1(dd, 4, 0, 1, 2, 3, 16); 146 | R1(dd, 3, 4, 0, 1, 2, 17); 147 | R1(dd, 2, 3, 4, 0, 1, 18); 148 | R1(dd, 1, 2, 3, 4, 0, 19); 149 | R2(dd, 0, 1, 2, 3, 4, 20); 150 | R2(dd, 4, 0, 1, 2, 3, 21); 151 | R2(dd, 3, 4, 0, 1, 2, 22); 152 | R2(dd, 2, 3, 4, 0, 1, 23); 153 | R2(dd, 1, 2, 3, 4, 0, 24); 154 | R2(dd, 0, 1, 2, 3, 4, 25); 155 | R2(dd, 4, 0, 1, 2, 3, 26); 156 | R2(dd, 3, 4, 0, 1, 2, 27); 157 | R2(dd, 2, 3, 4, 0, 1, 28); 158 | R2(dd, 1, 2, 3, 4, 0, 29); 159 | R2(dd, 0, 1, 2, 3, 4, 30); 160 | R2(dd, 4, 0, 1, 2, 3, 31); 161 | R2(dd, 3, 4, 0, 1, 2, 32); 162 | R2(dd, 2, 3, 4, 0, 1, 33); 163 | R2(dd, 1, 2, 3, 4, 0, 34); 164 | R2(dd, 0, 1, 2, 3, 4, 35); 165 | R2(dd, 4, 0, 1, 2, 3, 36); 166 | R2(dd, 3, 4, 0, 1, 2, 37); 167 | R2(dd, 2, 3, 4, 0, 1, 38); 168 | R2(dd, 1, 2, 3, 4, 0, 39); 169 | R3(dd, 0, 1, 2, 3, 4, 40); 170 | R3(dd, 4, 0, 1, 2, 3, 41); 171 | R3(dd, 3, 4, 0, 1, 2, 42); 172 | R3(dd, 2, 3, 4, 0, 1, 43); 173 | R3(dd, 1, 2, 3, 4, 0, 44); 174 | R3(dd, 0, 1, 2, 3, 4, 45); 175 | R3(dd, 4, 0, 1, 2, 3, 46); 176 | R3(dd, 3, 4, 0, 1, 2, 47); 177 | R3(dd, 2, 3, 4, 0, 1, 48); 178 | R3(dd, 1, 2, 3, 4, 0, 49); 179 | R3(dd, 0, 1, 2, 3, 4, 50); 180 | R3(dd, 4, 0, 1, 2, 3, 51); 181 | R3(dd, 3, 4, 0, 1, 2, 52); 182 | R3(dd, 2, 3, 4, 0, 1, 53); 183 | R3(dd, 1, 2, 3, 4, 0, 54); 184 | R3(dd, 0, 1, 2, 3, 4, 55); 185 | R3(dd, 4, 0, 1, 2, 3, 56); 186 | R3(dd, 3, 4, 0, 1, 2, 57); 187 | R3(dd, 2, 3, 4, 0, 1, 58); 188 | R3(dd, 1, 2, 3, 4, 0, 59); 189 | R4(dd, 0, 1, 2, 3, 4, 60); 190 | R4(dd, 4, 0, 1, 2, 3, 61); 191 | R4(dd, 3, 4, 0, 1, 2, 62); 192 | R4(dd, 2, 3, 4, 0, 1, 63); 193 | R4(dd, 1, 2, 3, 4, 0, 64); 194 | R4(dd, 0, 1, 2, 3, 4, 65); 195 | R4(dd, 4, 0, 1, 2, 3, 66); 196 | R4(dd, 3, 4, 0, 1, 2, 67); 197 | R4(dd, 2, 3, 4, 0, 1, 68); 198 | R4(dd, 1, 2, 3, 4, 0, 69); 199 | R4(dd, 0, 1, 2, 3, 4, 70); 200 | R4(dd, 4, 0, 1, 2, 3, 71); 201 | R4(dd, 3, 4, 0, 1, 2, 72); 202 | R4(dd, 2, 3, 4, 0, 1, 73); 203 | R4(dd, 1, 2, 3, 4, 0, 74); 204 | R4(dd, 0, 1, 2, 3, 4, 75); 205 | R4(dd, 4, 0, 1, 2, 3, 76); 206 | R4(dd, 3, 4, 0, 1, 2, 77); 207 | R4(dd, 2, 3, 4, 0, 1, 78); 208 | R4(dd, 1, 2, 3, 4, 0, 79); 209 | /* Add the working vars back into context.state[] */ 210 | state[0] += dd[0]; 211 | state[1] += dd[1]; 212 | state[2] += dd[2]; 213 | state[3] += dd[3]; 214 | state[4] += dd[4]; 215 | } 216 | 217 | 218 | /** 219 | * 220 | * SHA1Init - Initialize new context 221 | */ 222 | public void init() { 223 | /* SHA1 initialization constants */ 224 | state[0] = 0x67452301; 225 | state[1] = 0xEFCDAB89; 226 | state[2] = 0x98BADCFE; 227 | state[3] = 0x10325476; 228 | state[4] = 0xC3D2E1F0; 229 | count = 0; 230 | digestBits = new byte[20]; 231 | digestValid = false; 232 | blockIndex = 0; 233 | } 234 | 235 | /** 236 | * Add one byte to the digest. When this is implemented all of the abstract class methods end up 237 | * calling this method for types other than bytes. 238 | */ 239 | public synchronized void update(byte b) { 240 | int mask = (8 * (blockIndex & 3)); 241 | 242 | count += 8; 243 | block[blockIndex >> 2] &= ~(0xff << mask); 244 | block[blockIndex >> 2] |= (b & 0xff) << mask; 245 | blockIndex++; 246 | if (blockIndex == 64) { 247 | transform(); 248 | blockIndex = 0; 249 | } 250 | } 251 | 252 | 253 | /** 254 | * Complete processing on the message digest. 255 | */ 256 | public void finish() { 257 | byte bits[] = new byte[8]; 258 | int i; 259 | 260 | for (i = 0; i < 8; i++) { 261 | bits[i] = (byte) ((count >>> (((7 - i) * 8))) & 0xff); 262 | } 263 | 264 | update((byte) 128); 265 | while (blockIndex != 56) 266 | update((byte) 0); 267 | // This should cause a transform to happen. 268 | update(bits); 269 | for (i = 0; i < 20; i++) { 270 | digestBits[i] = (byte) ((state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xff); 271 | } 272 | digestValid = true; 273 | } 274 | 275 | /** Return a string that identifies this algorithm */ 276 | public String getAlg() { 277 | return "SHA1"; 278 | } 279 | 280 | /** 281 | * Print out the digest in a form that can be easily compared to the test vectors. 282 | */ 283 | public String digout() { 284 | StringBuffer sb = new StringBuffer(); 285 | for (int i = 0; i < 20; i++) { 286 | char c1, c2; 287 | 288 | c1 = (char) ((digestBits[i] >>> 4) & 0xf); 289 | c2 = (char) (digestBits[i] & 0xf); 290 | c1 = (char) ((c1 > 9) ? 'a' + (c1 - 10) : '0' + c1); 291 | c2 = (char) ((c2 > 9) ? 'a' + (c2 - 10) : '0' + c2); 292 | sb.append(c1); 293 | sb.append(c2); 294 | /* 295 | * if (((i+1) % 4) == 0) sb.append(' '); 296 | */ 297 | } 298 | return sb.toString(); 299 | } 300 | 301 | // 输出中间状态 302 | public String dumpTempState() { 303 | StringBuilder sb = new StringBuilder(); 304 | final int count = 5; 305 | for (int index = 0; index < count; ++index) { 306 | for (int i = 0; i < 4; ++i) { 307 | sb.append(String.format("%02x", (byte)(state[index] >>> (i * 8)))); 308 | } 309 | } 310 | return sb.toString(); 311 | } 312 | } 313 | 314 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/demo/Demo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. To change this 3 | * template file, choose Tools | Templates and open the template in the editor. 4 | */ 5 | package com.qcloud.cos.demo; 6 | 7 | import java.nio.charset.Charset; 8 | 9 | import com.qcloud.cos.*; 10 | import com.qcloud.cos.common_utils.CommonFileUtils; 11 | import com.qcloud.cos.meta.FileAuthority; 12 | import com.qcloud.cos.meta.InsertOnly; 13 | import com.qcloud.cos.request.CreateFolderRequest; 14 | import com.qcloud.cos.request.DelFileRequest; 15 | import com.qcloud.cos.request.DelFolderRequest; 16 | import com.qcloud.cos.request.GetFileLocalRequest; 17 | import com.qcloud.cos.request.ListFolderRequest; 18 | import com.qcloud.cos.request.MoveFileRequest; 19 | import com.qcloud.cos.request.StatFileRequest; 20 | import com.qcloud.cos.request.StatFolderRequest; 21 | import com.qcloud.cos.request.UpdateFileRequest; 22 | import com.qcloud.cos.request.UpdateFolderRequest; 23 | import com.qcloud.cos.request.UploadFileRequest; 24 | import com.qcloud.cos.sign.Credentials; 25 | 26 | 27 | /** 28 | * @author chengwu cos Demo代码 29 | */ 30 | public class Demo { 31 | 32 | public static void main(String[] args) throws Exception { 33 | 34 | // 设置用户属性, 包括appid, secretId和SecretKey 35 | // 这些属性可以通过cos控制台获取(https://console.qcloud.com/cos) 36 | long appId = 1000000; 37 | String secretId = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"; 38 | String secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxx"; 39 | // 设置要操作的bucket 40 | String bucketName = "xxxxxxxxx"; 41 | // 初始化客户端配置 42 | ClientConfig clientConfig = new ClientConfig(); 43 | // 设置bucket所在的区域,比如广州(gz), 天津(tj) 44 | clientConfig.setRegion("gz"); 45 | // 初始化秘钥信息 46 | Credentials cred = new Credentials(appId, secretId, secretKey); 47 | // 初始化cosClient 48 | COSClient cosClient = new COSClient(clientConfig, cred); 49 | /////////////////////////////////////////////////////////////// 50 | // 文件操作 // 51 | /////////////////////////////////////////////////////////////// 52 | // 1. 上传文件(将本地文件上传到COS) 53 | // 将本地的local_file_1.txt上传到bucket下的根分区下,并命名为sample_file.txt 54 | // 默认不覆盖, 如果cos上已有文件, 则返回错误 55 | String cosFilePath = "/sample_file.txt"; 56 | String localFilePath1 = "src/test/resources/bigfile.txt"; 57 | UploadFileRequest uploadFileRequest = 58 | new UploadFileRequest(bucketName, cosFilePath, localFilePath1); 59 | uploadFileRequest.setEnableShaDigest(false); 60 | String uploadFileRet = cosClient.uploadFile(uploadFileRequest); 61 | System.out.println("upload file ret:" + uploadFileRet); 62 | 63 | // 2. 下载文件 64 | String localPathDown = "src/test/resources/local_file_down.txt"; 65 | GetFileLocalRequest getFileLocalRequest = 66 | new GetFileLocalRequest(bucketName, cosFilePath, localPathDown); 67 | getFileLocalRequest.setUseCDN(false); 68 | getFileLocalRequest.setReferer("*.myweb.cn"); 69 | String getFileResult = cosClient.getFileLocal(getFileLocalRequest); 70 | System.out.println("getFileResult:" + getFileResult); 71 | 72 | // 3. 上传文件(将内存数据上传到COS) 73 | // 示例程序的内存数据来自于本地文件,将本地文件整体读入内存,如果本地文件过大,可能导致OOM 74 | // 因此如果数据在本地,建议直接用方法1 75 | String localFilePath2 = "src/test/resources/local_file_2.txt"; 76 | byte[] contentBuffer = CommonFileUtils.getFileContent(localFilePath2) 77 | .getBytes(Charset.forName(("ISO-8859-1"))); 78 | UploadFileRequest overWriteFileRequest = 79 | new UploadFileRequest(bucketName, cosFilePath, contentBuffer); 80 | String overWriteFileRet = cosClient.uploadFile(overWriteFileRequest); 81 | System.out.println("overwrite file ret:" + overWriteFileRet); 82 | 83 | // 4. 获取文件属性 84 | StatFileRequest statFileRequest = new StatFileRequest(bucketName, cosFilePath); 85 | String statFileRet = cosClient.statFile(statFileRequest); 86 | System.out.println("stat file ret:" + statFileRet); 87 | 88 | // 5. 更新文件属性 89 | UpdateFileRequest updateFileRequest = new UpdateFileRequest(bucketName, cosFilePath); 90 | updateFileRequest.setBizAttr("测试目录"); 91 | updateFileRequest.setAuthority(FileAuthority.WPRIVATE); 92 | updateFileRequest.setCacheControl("no cache"); 93 | updateFileRequest.setContentDisposition("cos_sample.txt"); 94 | updateFileRequest.setContentLanguage("english"); 95 | updateFileRequest.setContentType("application/json"); 96 | updateFileRequest.setXCosMeta("x-cos-meta-xxx", "xxx"); 97 | updateFileRequest.setXCosMeta("x-cos-meta-yyy", "yyy"); 98 | updateFileRequest.setContentEncoding("gzip"); 99 | String updateFileRet = cosClient.updateFile(updateFileRequest); 100 | System.out.println("update file ret:" + updateFileRet); 101 | 102 | // 6. 更新文件后再次获取属性 103 | statFileRet = cosClient.statFile(statFileRequest); 104 | System.out.println("stat file ret:" + statFileRet); 105 | 106 | // 6.1 move文件,从/sample_file.txt移动为./sample_file.txt.bak 107 | String dstFilePath = cosFilePath + ".bak"; 108 | MoveFileRequest moveRequest = new MoveFileRequest(bucketName, cosFilePath, dstFilePath); 109 | String moveFileRet = cosClient.moveFile(moveRequest); 110 | System.out.println("first move file ret:" + moveFileRet); 111 | // 6.2 在从/sample_file.txt.bak移动为/sample_file.txt 112 | moveRequest = new MoveFileRequest(bucketName, dstFilePath, cosFilePath); 113 | moveFileRet = cosClient.moveFile(moveRequest); 114 | System.out.println("second move file ret:" + moveFileRet); 115 | 116 | // 7. 删除文件 117 | DelFileRequest delFileRequest = new DelFileRequest(bucketName, cosFilePath); 118 | String delFileRet = cosClient.delFile(delFileRequest); 119 | System.out.println("del file ret:" + delFileRet); 120 | 121 | /////////////////////////////////////////////////////////////// 122 | // 目录操作 // 123 | /////////////////////////////////////////////////////////////// 124 | // 1. 生成目录, 目录名为sample_folder 125 | String cosFolderPath = "/xxsample_folder/"; 126 | CreateFolderRequest createFolderRequest = 127 | new CreateFolderRequest(bucketName, cosFolderPath); 128 | String createFolderRet = cosClient.createFolder(createFolderRequest); 129 | System.out.println("create folder ret:" + createFolderRet); 130 | 131 | // 2. 更新目录的biz_attr属性 132 | UpdateFolderRequest updateFolderRequest = 133 | new UpdateFolderRequest(bucketName, cosFolderPath); 134 | updateFolderRequest.setBizAttr("这是一个测试目录"); 135 | String updateFolderRet = cosClient.updateFolder(updateFolderRequest); 136 | System.out.println("update folder ret:" + updateFolderRet); 137 | 138 | // 3. 获取目录属性 139 | StatFolderRequest statFolderRequest = new StatFolderRequest(bucketName, cosFolderPath); 140 | String statFolderRet = cosClient.statFolder(statFolderRequest); 141 | System.out.println("stat folder ret:" + statFolderRet); 142 | 143 | // 4. list目录, 获取目录下的成员 144 | ListFolderRequest listFolderRequest = new ListFolderRequest(bucketName, cosFolderPath); 145 | String listFolderRet = cosClient.listFolder(listFolderRequest); 146 | System.out.println("list folder ret:" + listFolderRet); 147 | 148 | // 5. 删除目录 149 | DelFolderRequest delFolderRequest = new DelFolderRequest(bucketName, cosFolderPath); 150 | String delFolderRet = cosClient.delFolder(delFolderRequest); 151 | System.out.println("del folder ret:" + delFolderRet); 152 | 153 | // 关闭释放资源 154 | cosClient.shutdown(); 155 | System.out.println("shutdown!"); 156 | 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/exception/AbstractCosException.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.exception; 2 | 3 | import org.json.JSONObject; 4 | 5 | import com.qcloud.cos.http.ResponseBodyKey; 6 | 7 | /** 8 | * 封装cos异常 9 | * @author chengwu 10 | * 11 | */ 12 | public abstract class AbstractCosException extends Exception { 13 | 14 | private static final long serialVersionUID = 7547532865194837136L; 15 | 16 | private CosExceptionType type; 17 | 18 | public AbstractCosException(CosExceptionType type, String message) { 19 | super(message); 20 | this.type = type; 21 | } 22 | 23 | public CosExceptionType getType() { 24 | return type; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | JSONObject responseObj = new JSONObject(); 30 | responseObj.put(ResponseBodyKey.CODE, type.getErrorCode()); 31 | responseObj.put(ResponseBodyKey.MESSAGE, getMessage()); 32 | return responseObj.toString(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/exception/CosExceptionType.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.exception; 2 | 3 | import com.qcloud.cos.ErrorCode; 4 | 5 | // 枚举cos异常类型 6 | public enum CosExceptionType { 7 | // 参数异常 8 | PARAM_EXCEPTION(ErrorCode.PARAMS_ERROR, "param_excepiton"), 9 | // 网络异常 10 | NETWORK_EXCEPITON(ErrorCode.NETWORK_ERROR, "network_excepiton"), 11 | // 服务端异常 12 | SERVER_EXCEPTION(ErrorCode.SERVER_ERROR, "server_exception"), 13 | // 其他未知异常 14 | UNKNOWN_EXCEPTION(ErrorCode.UNKNOWN_ERROR, "unknown_exception"); 15 | 16 | private int errorCode; 17 | private String exceptionStr; 18 | 19 | private CosExceptionType(int errorCode, String exceptionStr) { 20 | this.errorCode = errorCode; 21 | this.exceptionStr = exceptionStr; 22 | } 23 | 24 | public int getErrorCode() { 25 | return errorCode; 26 | } 27 | 28 | public String getExceptionStr() { 29 | return exceptionStr; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/exception/NetworkException.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.exception; 2 | 3 | // 网络异常(如网络故障,导致无法连接服务端) 4 | public class NetworkException extends AbstractCosException { 5 | 6 | private static final long serialVersionUID = -6662661467437143397L; 7 | 8 | public NetworkException(String message) { 9 | super(CosExceptionType.NETWORK_EXCEPITON, message); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/exception/ParamException.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.exception; 2 | 3 | // 参数异常 4 | public class ParamException extends AbstractCosException { 5 | 6 | private static final long serialVersionUID = 216921496331691543L; 7 | 8 | public ParamException(String message) { 9 | super(CosExceptionType.PARAM_EXCEPTION, message); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/exception/ServerException.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.exception; 2 | 3 | // 服务端异常(如返回404deng) 4 | public class ServerException extends AbstractCosException { 5 | 6 | private static final long serialVersionUID = -4536038808919814914L; 7 | 8 | public ServerException(String message) { 9 | super(CosExceptionType.SERVER_EXCEPTION, message); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/exception/UnknownException.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.exception; 2 | 3 | // 未知异常 4 | public class UnknownException extends AbstractCosException { 5 | 6 | private static final long serialVersionUID = 4303770859616883146L; 7 | 8 | public UnknownException(String message) { 9 | super(CosExceptionType.UNKNOWN_EXCEPTION, message); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/AbstractCosHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | import java.io.InputStream; 4 | 5 | import org.apache.http.HttpHost; 6 | import org.apache.http.client.HttpClient; 7 | import org.apache.http.client.config.RequestConfig; 8 | import org.apache.http.impl.client.HttpClientBuilder; 9 | import org.apache.http.impl.client.HttpClients; 10 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 11 | 12 | import com.qcloud.cos.ClientConfig; 13 | import com.qcloud.cos.exception.AbstractCosException; 14 | import com.qcloud.cos.exception.ParamException; 15 | 16 | public abstract class AbstractCosHttpClient { 17 | protected ClientConfig config; 18 | protected HttpClient httpClient; 19 | 20 | protected PoolingHttpClientConnectionManager connectionManager; 21 | protected IdleConnectionMonitorThread idleConnectionMonitor; 22 | 23 | protected RequestConfig requestConfig; 24 | 25 | public AbstractCosHttpClient(ClientConfig config) { 26 | super(); 27 | this.config = config; 28 | this.connectionManager = new PoolingHttpClientConnectionManager(); 29 | this.connectionManager.setMaxTotal(config.getMaxConnectionsCount()); 30 | this.connectionManager.setDefaultMaxPerRoute(config.getMaxConnectionsCount()); 31 | this.connectionManager.setValidateAfterInactivity(1); 32 | HttpClientBuilder httpClientBuilder = 33 | HttpClients.custom().setConnectionManager(connectionManager); 34 | if (config.getHttpProxyIp() != null && config.getHttpProxyPort() != 0) { 35 | HttpHost proxy = new HttpHost(config.getHttpProxyIp(), config.getHttpProxyPort()); 36 | httpClientBuilder.setProxy(proxy); 37 | } 38 | this.httpClient = httpClientBuilder.build(); 39 | this.requestConfig = RequestConfig.custom() 40 | .setConnectionRequestTimeout(this.config.getConnectionRequestTimeout()) 41 | .setConnectTimeout(this.config.getConnectionTimeout()) 42 | .setSocketTimeout(this.config.getSocketTimeout()).build(); 43 | this.idleConnectionMonitor = new IdleConnectionMonitorThread(this.connectionManager); 44 | this.idleConnectionMonitor.setDaemon(true); 45 | this.idleConnectionMonitor.start(); 46 | } 47 | 48 | protected abstract String sendPostRequest(HttpRequest httpRequest) throws AbstractCosException; 49 | 50 | protected abstract String sendGetRequest(HttpRequest httpRequest) throws AbstractCosException; 51 | 52 | public String sendHttpRequest(HttpRequest httpRequest) throws AbstractCosException { 53 | 54 | HttpMethod method = httpRequest.getMethod(); 55 | if (method == HttpMethod.POST) { 56 | return sendPostRequest(httpRequest); 57 | } else if (method == HttpMethod.GET) { 58 | return sendGetRequest(httpRequest); 59 | } else { 60 | throw new ParamException("Unsupported Http Method"); 61 | } 62 | } 63 | 64 | public abstract InputStream getFileInputStream(HttpRequest httpRequest) 65 | throws AbstractCosException; 66 | 67 | public void shutdown() { 68 | this.idleConnectionMonitor.shutdown(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/DefaultCosHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.URISyntaxException; 6 | import java.nio.charset.Charset; 7 | import java.util.Map; 8 | 9 | import org.apache.http.Consts; 10 | import org.apache.http.HttpEntity; 11 | import org.apache.http.HttpMessage; 12 | import org.apache.http.HttpResponse; 13 | import org.apache.http.ParseException; 14 | import org.apache.http.client.methods.HttpGet; 15 | import org.apache.http.client.methods.HttpPost; 16 | import org.apache.http.client.utils.URIBuilder; 17 | import org.apache.http.entity.ContentType; 18 | import org.apache.http.entity.StringEntity; 19 | import org.apache.http.entity.mime.MultipartEntityBuilder; 20 | import org.apache.http.util.EntityUtils; 21 | import org.json.JSONException; 22 | import org.json.JSONObject; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.qcloud.cos.ClientConfig; 27 | import com.qcloud.cos.exception.AbstractCosException; 28 | import com.qcloud.cos.exception.ParamException; 29 | import com.qcloud.cos.exception.ServerException; 30 | import com.qcloud.cos.exception.UnknownException; 31 | import com.qcloud.cos.meta.COSObjectInputStream; 32 | 33 | /** 34 | * @author chengwu 封装Http发送请求类 35 | */ 36 | public class DefaultCosHttpClient extends AbstractCosHttpClient { 37 | 38 | private static final Logger LOG = LoggerFactory.getLogger(DefaultCosHttpClient.class); 39 | 40 | public DefaultCosHttpClient(ClientConfig config) { 41 | super(config); 42 | } 43 | 44 | // 获得异常发生时的返回信息 45 | private String getExceptionMsg(HttpRequest httpRequest, String exceptionStr) { 46 | String errMsg = new StringBuilder("HttpRequest:").append(httpRequest.toString()) 47 | .append("\nException:").append(exceptionStr).toString(); 48 | LOG.error(errMsg); 49 | return errMsg; 50 | } 51 | 52 | /** 53 | * Get请求函数 54 | * 55 | * @param url 56 | * @param headers 额外添加的Http头部 57 | * @param params GET请求的参数 58 | * @return Cos服务器返回的字符串 59 | * @throws Exception 60 | */ 61 | @Override 62 | protected String sendGetRequest(HttpRequest httpRequest) throws AbstractCosException { 63 | String url = httpRequest.getUrl(); 64 | HttpGet httpGet = null; 65 | String responseStr = ""; 66 | int retry = 0; 67 | int maxRetryCount = this.config.getMaxFailedRetry(); 68 | while (retry < maxRetryCount) { 69 | try { 70 | URIBuilder urlBuilder = new URIBuilder(url); 71 | for (String paramKey : httpRequest.getParams().keySet()) { 72 | urlBuilder.addParameter(paramKey, httpRequest.getParams().get(paramKey)); 73 | } 74 | httpGet = new HttpGet(urlBuilder.build()); 75 | } catch (URISyntaxException e) { 76 | String errMsg = "Invalid url:" + url; 77 | LOG.error(errMsg); 78 | throw new ParamException(errMsg); 79 | } 80 | 81 | httpGet.setConfig(requestConfig); 82 | setHeaders(httpGet, httpRequest.getHeaders()); 83 | 84 | HttpResponse httpResponse = null; 85 | try { 86 | httpResponse = httpClient.execute(httpGet); 87 | int http_statuscode = httpResponse.getStatusLine().getStatusCode(); 88 | 89 | if (http_statuscode >= 500 && http_statuscode <= 599) { 90 | String errMsg = String.format("http status code is %d, response body: %s", 91 | http_statuscode, getResponseString(httpResponse)); 92 | throw new IOException(errMsg); 93 | } 94 | 95 | responseStr = getResponseString(httpResponse); 96 | new JSONObject(responseStr); 97 | httpGet.releaseConnection(); 98 | return responseStr; 99 | } catch (ParseException e) { 100 | httpGet.abort(); 101 | ++retry; 102 | if (retry == maxRetryCount) { 103 | String errMsg = getExceptionMsg(httpRequest, e.toString()); 104 | throw new ServerException(errMsg); 105 | } 106 | } catch (IOException e) { 107 | httpGet.abort(); 108 | ++retry; 109 | if (retry == maxRetryCount) { 110 | String errMsg = getExceptionMsg(httpRequest, e.toString()); 111 | throw new ServerException(errMsg); 112 | } 113 | } catch (JSONException e) { 114 | httpGet.abort(); 115 | String errMsg = String.format( 116 | "server response is not json, httpRequest: %s, httpResponse: %s, responseStr: %s", 117 | httpRequest.toString(), httpResponse.toString(), responseStr); 118 | throw new ServerException(errMsg); 119 | } 120 | } 121 | return responseStr; 122 | 123 | } 124 | 125 | @Override 126 | protected String sendPostRequest(HttpRequest httpRequest) throws AbstractCosException { 127 | String url = httpRequest.getUrl(); 128 | String responseStr = ""; 129 | int retry = 0; 130 | int maxRetryCount = this.config.getMaxFailedRetry(); 131 | while (retry < maxRetryCount) { 132 | HttpPost httpPost = new HttpPost(url); 133 | httpPost.setConfig(requestConfig); 134 | 135 | Map params = httpRequest.getParams(); 136 | setHeaders(httpPost, httpRequest.getHeaders()); 137 | 138 | if (httpRequest.getContentType() == HttpContentType.APPLICATION_JSON) { 139 | setJsonEntity(httpPost, params); 140 | } else if (httpRequest.getContentType() == HttpContentType.MULTIPART_FORM_DATA) { 141 | try { 142 | setMultiPartEntity(httpPost, params); 143 | } catch (Exception e) { 144 | throw new UnknownException(e.toString()); 145 | } 146 | } 147 | 148 | HttpResponse httpResponse = null; 149 | try { 150 | httpResponse = httpClient.execute(httpPost); 151 | int http_statuscode = httpResponse.getStatusLine().getStatusCode(); 152 | if (http_statuscode >= 500 && http_statuscode <= 599) { 153 | String errMsg = String.format("http status code is %d, response body: %s", 154 | http_statuscode, getResponseString(httpResponse)); 155 | throw new IOException(errMsg); 156 | } 157 | responseStr = getResponseString(httpResponse); 158 | new JSONObject(responseStr); 159 | httpPost.releaseConnection(); 160 | return responseStr; 161 | } catch (ParseException e) { 162 | httpPost.abort(); 163 | ++retry; 164 | if (retry == maxRetryCount) { 165 | String errMsg = getExceptionMsg(httpRequest, e.toString()); 166 | throw new ServerException(errMsg); 167 | } 168 | } catch (IOException e) { 169 | httpPost.abort(); 170 | ++retry; 171 | if (retry == maxRetryCount) { 172 | String errMsg = getExceptionMsg(httpRequest, e.toString()); 173 | throw new ServerException(errMsg); 174 | } 175 | } catch (JSONException e) { 176 | httpPost.abort(); 177 | String errMsg = String.format( 178 | "server response is not json, httpRequest: %s, httpResponse: %s, responseStr: %s", 179 | httpRequest.toString(), httpResponse.toString(), responseStr); 180 | throw new ServerException(errMsg); 181 | } 182 | } 183 | return responseStr; 184 | } 185 | 186 | @Override 187 | public InputStream getFileInputStream(HttpRequest httpRequest) throws AbstractCosException { 188 | String url = httpRequest.getUrl(); 189 | int retry = 0; 190 | int maxRetryCount = this.config.getMaxFailedRetry(); 191 | while (retry < maxRetryCount) { 192 | HttpGet httpGet = null; 193 | try { 194 | URIBuilder urlBuilder = new URIBuilder(url); 195 | for (String paramKey : httpRequest.getParams().keySet()) { 196 | urlBuilder.addParameter(paramKey, httpRequest.getParams().get(paramKey)); 197 | } 198 | httpGet = new HttpGet(urlBuilder.build()); 199 | } catch (URISyntaxException e) { 200 | String errMsg = "Invalid url:" + url; 201 | LOG.error(errMsg); 202 | throw new ParamException(errMsg); 203 | } 204 | 205 | httpGet.setConfig(requestConfig); 206 | setHeaders(httpGet, httpRequest.getHeaders()); 207 | try { 208 | HttpResponse httpResponse = httpClient.execute(httpGet); 209 | int http_statuscode = httpResponse.getStatusLine().getStatusCode(); 210 | if (http_statuscode >= 500 && http_statuscode <= 599) { 211 | String errMsg = String.format("http status code is %d, response body: %s", 212 | http_statuscode, getResponseString(httpResponse)); 213 | throw new IOException(errMsg); 214 | } 215 | if (http_statuscode != 200 && http_statuscode != 206) { 216 | String responseStr = getResponseString(httpResponse); 217 | String errMsg = String.format( 218 | "getFileinputstream failed, httpRequest: %s, httpResponse: %s, responseStr: %s", 219 | httpRequest.toString(), httpResponse.toString(), responseStr); 220 | 221 | httpGet.releaseConnection(); 222 | throw new ServerException(errMsg); 223 | } 224 | HttpEntity entity = httpResponse.getEntity(); 225 | COSObjectInputStream cosObjectInputStream = 226 | new COSObjectInputStream(entity.getContent(), httpGet); 227 | return cosObjectInputStream; 228 | } catch (ParseException e) { 229 | ++retry; 230 | httpGet.abort(); 231 | if (retry == maxRetryCount) { 232 | String errMsg = getExceptionMsg(httpRequest, e.toString()); 233 | throw new ServerException(errMsg); 234 | } 235 | } catch (IOException e) { 236 | ++retry; 237 | httpGet.abort(); 238 | if (retry == maxRetryCount) { 239 | String errMsg = getExceptionMsg(httpRequest, e.toString()); 240 | throw new ServerException(errMsg); 241 | } 242 | } 243 | } 244 | // never will reach here 245 | return null; 246 | } 247 | 248 | private String getResponseString(HttpResponse httpResponse) throws ParseException, IOException { 249 | String httpResponseStr = null; 250 | HttpEntity httpEntity = httpResponse.getEntity(); 251 | if (httpEntity != null) { 252 | httpResponseStr = EntityUtils.toString(httpEntity, "UTF-8"); 253 | } 254 | 255 | if (httpResponseStr == null) { 256 | return ""; 257 | } else { 258 | return httpResponseStr; 259 | } 260 | } 261 | 262 | private void setJsonEntity(HttpPost httpPost, Map params) { 263 | ContentType utf8TextPlain = ContentType.create("text/plain", Consts.UTF_8); 264 | String postJsonStr = new JSONObject(params).toString(); 265 | StringEntity stringEntity = new StringEntity(postJsonStr, utf8TextPlain); 266 | httpPost.setEntity(stringEntity); 267 | } 268 | 269 | private void setMultiPartEntity(HttpPost httpPost, Map params) 270 | throws Exception { 271 | ContentType utf8TextPlain = ContentType.create("text/plain", Consts.UTF_8); 272 | MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); 273 | for (String paramKey : params.keySet()) { 274 | if (paramKey.equals(RequestBodyKey.FILE_CONTENT)) { 275 | entityBuilder.addBinaryBody(RequestBodyKey.FILE_CONTENT, params 276 | .get(RequestBodyKey.FILE_CONTENT).getBytes(Charset.forName("ISO-8859-1"))); 277 | } else { 278 | entityBuilder.addTextBody(paramKey, params.get(paramKey), utf8TextPlain); 279 | } 280 | } 281 | httpPost.setEntity(entityBuilder.build()); 282 | } 283 | 284 | /** 285 | * 设置Http头部,同时添加上公共的类型,长连接,COS SDK标识 286 | * 287 | * @param message HTTP消息 288 | * @param headers 用户额外添加的HTTP头部 289 | */ 290 | private void setHeaders(HttpMessage message, Map headers) { 291 | message.setHeader(RequestHeaderKey.ACCEPT, RequestHeaderValue.Accept.ALL); 292 | message.setHeader(RequestHeaderKey.CONNECTION, RequestHeaderValue.Connection.KEEP_ALIVE); 293 | message.setHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 294 | 295 | if (headers != null) { 296 | for (String headerKey : headers.keySet()) { 297 | message.setHeader(headerKey, headers.get(headerKey)); 298 | } 299 | } 300 | } 301 | 302 | } 303 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/HttpContentType.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | public enum HttpContentType { 4 | 5 | APPLICATION_JSON("application/json"), 6 | 7 | MULTIPART_FORM_DATA("multipart/form-data"); 8 | 9 | private String contentType; 10 | 11 | private HttpContentType(String contentType) { 12 | this.contentType = contentType; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return this.contentType; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/HttpMethod.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | public enum HttpMethod { 4 | 5 | GET("GET"), 6 | 7 | POST("POST"), 8 | 9 | PUT("PUT"), 10 | 11 | DELETE("DELETE"); 12 | 13 | private String method; 14 | 15 | private HttpMethod(String method) { 16 | this.method = method; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return this.method; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/HttpRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | 7 | public class HttpRequest { 8 | private String url = ""; 9 | private HttpMethod method = HttpMethod.POST; 10 | private HttpContentType contentType = HttpContentType.MULTIPART_FORM_DATA; 11 | private Map headers = new LinkedHashMap(); 12 | private Map params = new LinkedHashMap(); 13 | 14 | public HttpRequest() { 15 | super(); 16 | } 17 | 18 | public String getUrl() { 19 | return url; 20 | } 21 | 22 | public void setUrl(String url) { 23 | this.url = url; 24 | } 25 | 26 | public HttpMethod getMethod() { 27 | return method; 28 | } 29 | 30 | public void setMethod(HttpMethod method) { 31 | this.method = method; 32 | } 33 | 34 | public HttpContentType getContentType() { 35 | return contentType; 36 | } 37 | 38 | public void setContentType(HttpContentType contentType) { 39 | this.contentType = contentType; 40 | } 41 | 42 | public Map getHeaders() { 43 | return headers; 44 | } 45 | 46 | public Map getParams() { 47 | return params; 48 | } 49 | 50 | public void addHeader(String key, String value) { 51 | this.headers.put(key, value); 52 | } 53 | 54 | public void addParam(String key, String value) { 55 | this.params.put(key, value); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | StringBuilder sb = new StringBuilder(); 61 | sb.append("url:").append(url).append(", method:").append(method).append(", ConentType:") 62 | .append(contentType.toString()).append("\n"); 63 | 64 | sb.append("Headers:\n"); 65 | for (Entry entry : headers.entrySet()) { 66 | sb.append("key:").append(entry.getKey()); 67 | sb.append(", value:").append(entry.getValue()); 68 | sb.append("\n"); 69 | } 70 | 71 | sb.append("params:\n"); 72 | for (Entry entry : params.entrySet()) { 73 | if (entry.getKey().equals(RequestBodyKey.FILE_CONTENT)) { 74 | continue; 75 | } 76 | sb.append("key:").append(entry.getKey()); 77 | sb.append(", value:").append(entry.getValue()); 78 | sb.append("\n"); 79 | } 80 | 81 | return sb.toString(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/IdleConnectionMonitorThread.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.apache.http.conn.HttpClientConnectionManager; 6 | 7 | // 用于监控空闲的连接池连接 8 | public final class IdleConnectionMonitorThread extends Thread { 9 | private final HttpClientConnectionManager connMgr; 10 | private volatile boolean shutdown; 11 | 12 | private static final int MONITOR_INTERVAL_MS = 2000; 13 | private static final int IDLE_ALIVE_MS = 5000; 14 | 15 | public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) { 16 | super(); 17 | this.connMgr = connMgr; 18 | this.shutdown = false; 19 | } 20 | 21 | @Override 22 | public void run() { 23 | try { 24 | while (!shutdown) { 25 | synchronized (this) { 26 | wait(MONITOR_INTERVAL_MS); 27 | // 关闭无效的连接 28 | connMgr.closeExpiredConnections(); 29 | // 关闭空闲时间超过IDLE_ALIVE_MS的连接 30 | connMgr.closeIdleConnections(IDLE_ALIVE_MS, TimeUnit.MILLISECONDS); 31 | } 32 | } 33 | } catch (InterruptedException e) { 34 | 35 | } 36 | } 37 | 38 | // 关闭后台连接 39 | public void shutdown() { 40 | shutdown = true; 41 | synchronized (this) { 42 | notifyAll(); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/RequestBodyKey.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | /** 4 | * @author chengwu 封装HTTP请求包体中的K-V对中key枚举值类 5 | */ 6 | public class RequestBodyKey { 7 | public static final String OP = "op"; 8 | public static final String BIZ_ATTR = "biz_attr"; 9 | public static final String UPDATE_FLAG = "flag"; 10 | public static final String AUTHORITY = "authority"; 11 | public static final String CUSTOM_HEADERS = "custom_headers"; 12 | public static final String INSERT_ONLY = "insertOnly"; 13 | public static final String DEST_FIELD = "dest_fileid"; 14 | public static final String TO_OVER_WRITE = "to_over_write"; 15 | public static final String PREFIX = "prefix"; 16 | public static final String NUM = "num"; 17 | public static final String CONTEXT = "context"; 18 | public static final String LIST_FLAG = "list_flag"; 19 | public static final String DELIMITER = "delimiter"; 20 | public static final String SHA = "sha"; 21 | public static final String FILE_CONTENT = "fileContent"; 22 | public static final String FILE_SIZE = "filesize"; 23 | public static final String SLICE_SIZE = "slice_size"; 24 | public static final String SESSION = "session"; 25 | public static final String OFFSET = "offset"; 26 | public static final String UPLOAD_PARTS = "uploadparts"; 27 | 28 | public class UploadParts { 29 | public static final String OFFSET = "offset"; 30 | public static final String DATA_LEN = "datalen"; 31 | public static final String DATA_SHA = "datasha"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/RequestBodyValue.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | /** 3 | * @author chengwu 4 | * 封装HTTP请求包体的k-v对中的value枚举值类 5 | */ 6 | public class RequestBodyValue { 7 | public class OP { 8 | public static final String CREATE = "create"; 9 | public static final String LIST = "list"; 10 | public static final String UPDATE = "update"; 11 | public static final String STAT = "stat"; 12 | public static final String MOVE = "move"; 13 | public static final String DELETE = "delete"; 14 | public static final String UPLOAD = "upload"; 15 | public static final String UPLOAD_SLICE_INIT = "upload_slice_init"; 16 | public static final String UPLOAD_SLICE_DATA = "upload_slice_data"; 17 | public static final String UPLOAD_SLICE_FINISH = "upload_slice_finish"; 18 | public static final String UPLOAD_SLICE_LIST = "upload_slice_list"; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/RequestHeaderKey.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | /** 3 | * @author chengwu 4 | * 封装HTTP请求头中的对应的K-V对中key枚举值类 5 | */ 6 | public class RequestHeaderKey { 7 | public static final String Authorization = "Authorization"; 8 | public static final String Content_TYPE = "Content-Type"; 9 | public static final String ACCEPT = "Accept"; 10 | public static final String CONNECTION = "Connection"; 11 | public static final String USER_AGENT = "User-Agent"; 12 | public static final String RANGE = "Range"; 13 | public static final String SIGN = "sign"; 14 | public static final String REFERER = "Referer"; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/RequestHeaderValue.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | /** 3 | * @author chengwu 4 | * 封装HTTP请求头中的对应的K-V对中value枚举值类 5 | */ 6 | 7 | public class RequestHeaderValue { 8 | public static class Method { 9 | public static final String POST = "POST"; 10 | public static final String GET = "GET"; 11 | public static final String PUT = "PUT"; 12 | public static final String DELETE = "DELETE"; 13 | } 14 | 15 | public static class ContentType { 16 | public static final String JSON = "application/json"; 17 | public static final String FORM_DATA = "multipart/form-data"; 18 | } 19 | 20 | public static class Accept { 21 | public static final String ALL = "*/*"; 22 | } 23 | 24 | public static class UserAgent { 25 | public static final String COS_FLAG = "qcloud-java-sdk"; 26 | } 27 | 28 | public static class Connection { 29 | public static final String KEEP_ALIVE = "Keep-Alive"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/http/ResponseBodyKey.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.http; 2 | 3 | /** 4 | * @author chengwu 5 | * 封装服务器端返回的应答包体的关键字枚举类 6 | */ 7 | public class ResponseBodyKey { 8 | public static final String CODE = "code"; 9 | public static final String MESSAGE = "message"; 10 | public static final String DATA = "data"; 11 | 12 | public class Data { 13 | public static final String SESSION = "session"; 14 | public static final String OFFSET = "offset"; 15 | public static final String SLICE_SIZE = "slice_size"; 16 | public static final String LISTPARTS = "listparts"; 17 | public static final String SERIAL_UPLOAD = "serial_upload"; 18 | public static final String ACCESS_URL = "access_url"; 19 | public static final String URL = "url"; 20 | public static final String RESOURCE_PATH = "resource_path"; 21 | public static final String NAME = "name"; 22 | public static final String BIZ_ATTR = "biz_attr"; 23 | public static final String FILESIZE = "filesize"; 24 | public static final String SHA = "sha"; 25 | public static final String CTIME = "ctime"; 26 | public static final String MTIME = "mtime"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/COSObjectInputStream.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | import org.apache.http.client.methods.HttpRequestBase; 7 | 8 | public class COSObjectInputStream extends InputStream { 9 | private InputStream in; 10 | private HttpRequestBase httpRequest; 11 | private boolean eof; 12 | 13 | public COSObjectInputStream(InputStream in, HttpRequestBase httpRequest) { 14 | super(); 15 | this.in = in; 16 | this.httpRequest = httpRequest; 17 | } 18 | 19 | private void doAbort() { 20 | if (httpRequest != null) { 21 | httpRequest.abort(); 22 | } 23 | closeStream(in); 24 | } 25 | 26 | public int read() throws IOException { 27 | int value = in.read(); 28 | if (value == -1) { 29 | eof = true; 30 | } 31 | return value; 32 | } 33 | 34 | public int read(byte[] b) throws IOException { 35 | return read(b, 0, b.length); 36 | } 37 | 38 | public int read(byte[] b, int off, int len) throws IOException { 39 | int value = in.read(b, off, len); 40 | if (value == -1) { 41 | eof = true; 42 | } 43 | return value; 44 | } 45 | 46 | public void reset() throws IOException { 47 | in.reset(); 48 | eof = false; 49 | } 50 | 51 | private void closeStream(InputStream in) { 52 | try { 53 | in.close(); 54 | } catch (IOException e) { 55 | } 56 | } 57 | 58 | 59 | public void close() throws IOException { 60 | if (eof) { 61 | closeStream(in); 62 | httpRequest.releaseConnection(); 63 | } else { 64 | doAbort(); 65 | } 66 | } 67 | 68 | public void abort() { 69 | doAbort(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/FileAuthority.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | public enum FileAuthority { 4 | INVALID("eInvalid"), 5 | WPRIVATE("eWRPrivate"), 6 | WPRIVATERPUBLIC("eWPrivateRPublic"); 7 | 8 | private String authority; 9 | 10 | private FileAuthority(String authority) { 11 | this.authority = authority; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return this.authority; 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/FileStat.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | import java.io.Serializable; 4 | 5 | import com.qcloud.cos.common_utils.CommonFileUtils; 6 | 7 | /** 8 | * @author chengwu 9 | * 文件状态, 包括文件大小,最近修改时间 10 | */ 11 | public class FileStat implements Serializable { 12 | 13 | private static final long serialVersionUID = -4572277446000987482L; 14 | 15 | public FileStat() {} 16 | 17 | public static FileStat getFileStat(String filePath) throws Exception { 18 | FileStat fileStat = new FileStat(); 19 | fileStat.fileSize = CommonFileUtils.getFileLength(filePath); 20 | fileStat.lastModifiedTime = CommonFileUtils.getFileLastModified(filePath); 21 | return fileStat; 22 | } 23 | 24 | private long fileSize; 25 | private long lastModifiedTime; 26 | 27 | public long getFileSize() { 28 | return fileSize; 29 | } 30 | 31 | public void setFileSize(long fileSize) { 32 | this.fileSize = fileSize; 33 | } 34 | 35 | public long getLastModifiedTime() { 36 | return lastModifiedTime; 37 | } 38 | 39 | public void setLastModifiedTime(long lastModifiedTime) { 40 | this.lastModifiedTime = lastModifiedTime; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object obj) { 45 | if (this == obj) { 46 | return true; 47 | } 48 | 49 | if (((FileStat) obj).getFileSize() == this.fileSize 50 | && ((FileStat) obj).getLastModifiedTime() == this.lastModifiedTime) { 51 | return true; 52 | } else { 53 | return false; 54 | } 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | int hashValue = 1; 60 | final int prime = 31; 61 | hashValue = prime * hashValue + new Long(fileSize).hashCode(); 62 | hashValue = prime * hashValue + new Long(lastModifiedTime).hashCode(); 63 | return hashValue; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/InsertOnly.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | public enum InsertOnly { 4 | OVER_WRITE, 5 | NO_OVER_WRITE 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/OverWrite.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | public enum OverWrite { 4 | NO_OVER_WRITE, 5 | OVER_WRITE 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/SliceFileDataTask.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.concurrent.Callable; 5 | 6 | import org.json.JSONObject; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.qcloud.cos.common_utils.CommonFileUtils; 11 | import com.qcloud.cos.exception.UnknownException; 12 | import com.qcloud.cos.http.AbstractCosHttpClient; 13 | import com.qcloud.cos.http.HttpContentType; 14 | import com.qcloud.cos.http.HttpMethod; 15 | import com.qcloud.cos.http.HttpRequest; 16 | import com.qcloud.cos.http.RequestBodyKey; 17 | import com.qcloud.cos.http.RequestBodyValue; 18 | import com.qcloud.cos.http.RequestHeaderKey; 19 | import com.qcloud.cos.sign.Credentials; 20 | import com.qcloud.cos.sign.Sign; 21 | 22 | /** 23 | * @author chengwu 执行分片上传的任务, 每一个任务由一个线程执行 24 | */ 25 | public class SliceFileDataTask implements Callable { 26 | 27 | private static final Logger LOG = LoggerFactory.getLogger(SliceFileDataTask.class); 28 | 29 | private int TaskId; 30 | private int sliceIndex; 31 | private UploadSliceFileContext context; 32 | private AbstractCosHttpClient httpClient; 33 | private Credentials cred; 34 | private String url; 35 | private long signExpired; 36 | 37 | public SliceFileDataTask(int taskId, int sliceIndex, UploadSliceFileContext context, 38 | AbstractCosHttpClient httpClient, Credentials cred, String url, long signExpired) { 39 | super(); 40 | TaskId = taskId; 41 | this.sliceIndex = sliceIndex; 42 | this.context = context; 43 | this.httpClient = httpClient; 44 | this.cred = cred; 45 | this.url = url; 46 | this.signExpired = signExpired; 47 | } 48 | 49 | @Override 50 | public JSONObject call() throws Exception { 51 | try { 52 | HttpRequest httpRequest = new HttpRequest(); 53 | SlicePart slicePart = context.sliceParts.get(sliceIndex); 54 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD_SLICE_DATA); 55 | if (this.context.isEnableShaDigest()) { 56 | httpRequest.addParam(RequestBodyKey.SHA, context.getEntireFileSha()); 57 | } 58 | 59 | httpRequest.addParam(RequestBodyKey.SESSION, context.getSessionId()); 60 | httpRequest.addParam(RequestBodyKey.OFFSET, String.valueOf(slicePart.getOffset())); 61 | String sliceContent = ""; 62 | if (this.context.isUploadFromBuffer()) { 63 | sliceContent = new String(context.getContentBuffer(), 64 | new Long(slicePart.getOffset()).intValue(), slicePart.getSliceSize(), 65 | Charset.forName("ISO-8859-1")); 66 | } else { 67 | sliceContent = CommonFileUtils.getFileContent(context.getLocalPath(), 68 | slicePart.getOffset(), slicePart.getSliceSize()); 69 | } 70 | httpRequest.addParam(RequestBodyKey.FILE_CONTENT, sliceContent); 71 | 72 | long signExpired = System.currentTimeMillis() / 1000 + this.signExpired; 73 | String sign = Sign.getPeriodEffectiveSign(context.getBucketName(), context.getCosPath(), 74 | this.cred, signExpired); 75 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 76 | 77 | httpRequest.setUrl(this.url); 78 | httpRequest.setMethod(HttpMethod.POST); 79 | httpRequest.setContentType(HttpContentType.MULTIPART_FORM_DATA); 80 | 81 | String resultStr = httpClient.sendHttpRequest(httpRequest); 82 | LOG.debug("sliceFileDataTask: " + this.toString() + ", result: " + resultStr); 83 | JSONObject resultJson = new JSONObject(resultStr); 84 | return resultJson; 85 | } catch (Exception e) { 86 | String errMsg = new StringBuilder().append("taskInfo:").append(this.toString()) 87 | .append(", Exception:").append(e.toString()).toString(); 88 | LOG.error(errMsg); 89 | throw new UnknownException(errMsg); 90 | } 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | StringBuilder sb = new StringBuilder(); 96 | sb.append("TaskId:").append(TaskId).append(", SliceIndex:").append(sliceIndex) 97 | .append(", localPath:").append(context.getLocalPath()).append(", uploadUrl:") 98 | .append(this.url); 99 | return sb.toString(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/SlicePart.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author chengwu 7 | * 分片信息,包括偏移量,分片大小,是否上传成功 8 | */ 9 | public class SlicePart implements Serializable { 10 | 11 | private static final long serialVersionUID = -7454131654081550885L; 12 | 13 | private long offset; 14 | private int sliceSize; 15 | private boolean uploadCompleted = false; 16 | 17 | public SlicePart() {} 18 | 19 | public long getOffset() { 20 | return offset; 21 | } 22 | 23 | public void setOffset(long offset) { 24 | this.offset = offset; 25 | } 26 | 27 | public int getSliceSize() { 28 | return sliceSize; 29 | } 30 | 31 | public void setSliceSize(int sliceSize) { 32 | this.sliceSize = sliceSize; 33 | } 34 | 35 | public boolean isUploadCompleted() { 36 | return uploadCompleted; 37 | } 38 | 39 | public void setUploadCompleted(boolean uploadCompleted) { 40 | this.uploadCompleted = uploadCompleted; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | int hashValue = 1; 46 | final int prime = 31; 47 | hashValue = prime * hashValue + new Long(offset).hashCode(); 48 | hashValue = prime * hashValue + new Integer(sliceSize).hashCode(); 49 | hashValue = prime * hashValue + new Boolean(uploadCompleted).hashCode(); 50 | return hashValue; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/meta/UploadSliceFileContext.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.meta; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import org.json.JSONArray; 8 | import org.json.JSONObject; 9 | 10 | import com.qcloud.cos.common_utils.CommonFileUtils; 11 | import com.qcloud.cos.exception.AbstractCosException; 12 | import com.qcloud.cos.exception.UnknownException; 13 | import com.qcloud.cos.http.ResponseBodyKey; 14 | import com.qcloud.cos.request.UploadSliceFileRequest; 15 | 16 | /** 17 | * 分片上传的上下文信息, 用于在多个步骤之间传递 18 | * 19 | * @author chengwu 20 | * 21 | */ 22 | public class UploadSliceFileContext { 23 | // bucket名 24 | private String bucketName = ""; 25 | // cos路径 26 | private String cosPath = ""; 27 | // 本地文件路径 28 | private String localPath = ""; 29 | // 文件bizAttr 30 | private String bizAttr = ""; 31 | // 是否覆盖 32 | private InsertOnly insertOnly = InsertOnly.NO_OVER_WRITE; 33 | // 分片大小 34 | private int sliceSize; 35 | // 文件大小 36 | private long fileSize = 0; 37 | 38 | private String url = ""; 39 | // 开启sha摘要 40 | private boolean enableShaDigest = true; 41 | // 任务数量 42 | private int taskNum = 1; 43 | // session 44 | private String sessionId = ""; 45 | // 全文sha 46 | private String entireFileSha = ""; 47 | 48 | private boolean serialUpload = true; 49 | 50 | // 标识是否是从内存中上传文件 51 | private boolean uploadFromBuffer = false; 52 | // 上传的buffer 53 | private byte[] contentBuffer = null; 54 | 55 | // 标识要全部的slice part,包含已经上传和为上传的,已经上传的slice part的完成属性会设置为true 56 | public ArrayList sliceParts; 57 | // 标识已经上传的slice parts 58 | private Set uploadCompletePartsSet; 59 | 60 | public UploadSliceFileContext(UploadSliceFileRequest request) throws AbstractCosException { 61 | this.bucketName = request.getBucketName(); 62 | this.cosPath = request.getCosPath(); 63 | this.uploadFromBuffer = request.isUploadFromBuffer(); 64 | if (request.isUploadFromBuffer()) { 65 | this.contentBuffer = request.getContentBufer(); 66 | } 67 | this.localPath = request.getLocalPath(); 68 | this.bizAttr = request.getBizAttr(); 69 | this.insertOnly = request.getInsertOnly(); 70 | this.sliceSize = request.getSliceSize(); 71 | this.taskNum = request.getTaskNum(); 72 | this.enableShaDigest = request.isEnableShaDigest(); 73 | this.sliceParts = new ArrayList(); 74 | this.uploadCompletePartsSet = new HashSet(); 75 | caculateFileSize(); 76 | } 77 | 78 | private void caculateFileSize() throws UnknownException { 79 | try { 80 | if (this.uploadFromBuffer) { 81 | this.fileSize = this.contentBuffer.length; 82 | } else { 83 | this.fileSize = CommonFileUtils.getFileLength(this.localPath); 84 | } 85 | } catch (Exception e) { 86 | throw new UnknownException("caculateFileSize error. " + e.toString()); 87 | } 88 | } 89 | 90 | // 切分文件 91 | public ArrayList prepareUploadPartsInfo() { 92 | int sliceCount = new Long((fileSize + (sliceSize - 1)) / sliceSize).intValue(); 93 | for (int sliceIndex = 0; sliceIndex < sliceCount; ++sliceIndex) { 94 | SlicePart part = new SlicePart(); 95 | long offset = (Long.valueOf(sliceIndex).longValue()) * sliceSize; 96 | part.setOffset(offset); 97 | if (sliceIndex != sliceCount - 1) { 98 | part.setSliceSize(sliceSize); 99 | } else { 100 | part.setSliceSize(new Long(fileSize - offset).intValue()); 101 | } 102 | part.setUploadCompleted(uploadCompletePartsSet.contains(offset)); 103 | sliceParts.add(part); 104 | } 105 | return sliceParts; 106 | } 107 | 108 | 109 | public void setUploadCompleteParts(JSONArray listPartsArry) { 110 | int listPartsLen = listPartsArry.length(); 111 | for (int listPartsIndex = 0; listPartsIndex < listPartsLen; ++listPartsIndex) { 112 | JSONObject listPartMember = listPartsArry.getJSONObject(listPartsIndex); 113 | long partOffset = listPartMember.getLong(ResponseBodyKey.Data.OFFSET); 114 | this.uploadCompletePartsSet.add(partOffset); 115 | } 116 | } 117 | 118 | 119 | public String getBucketName() { 120 | return bucketName; 121 | } 122 | 123 | public void setBucketName(String bucketName) { 124 | this.bucketName = bucketName; 125 | } 126 | 127 | public String getCosPath() { 128 | return cosPath; 129 | } 130 | 131 | public void setCosPath(String cosPath) { 132 | this.cosPath = cosPath; 133 | } 134 | 135 | public String getLocalPath() { 136 | return localPath; 137 | } 138 | 139 | public void setLocalPath(String localPath) { 140 | this.localPath = localPath; 141 | } 142 | 143 | public String getBizAttr() { 144 | return bizAttr; 145 | } 146 | 147 | public void setBizAttr(String bizAttr) { 148 | this.bizAttr = bizAttr; 149 | } 150 | 151 | public InsertOnly getInsertOnly() { 152 | return insertOnly; 153 | } 154 | 155 | public void setInsertOnly(InsertOnly insertOnly) { 156 | this.insertOnly = insertOnly; 157 | } 158 | 159 | public int getSliceSize() { 160 | return sliceSize; 161 | } 162 | 163 | public void setSliceSize(int sliceSize) { 164 | this.sliceSize = sliceSize; 165 | } 166 | 167 | public long getFileSize() { 168 | return fileSize; 169 | } 170 | 171 | public void setFileSize(long fileSize) { 172 | this.fileSize = fileSize; 173 | } 174 | 175 | public String getUrl() { 176 | return url; 177 | } 178 | 179 | public void setUrl(String url) { 180 | this.url = url; 181 | } 182 | 183 | public boolean isEnableShaDigest() { 184 | return enableShaDigest; 185 | } 186 | 187 | public void setEnableShaDigest(boolean enableShaDigest) { 188 | this.enableShaDigest = enableShaDigest; 189 | } 190 | 191 | public int getTaskNum() { 192 | return taskNum; 193 | } 194 | 195 | public void setTaskNum(int taskNum) { 196 | this.taskNum = taskNum; 197 | } 198 | 199 | public String getSessionId() { 200 | return sessionId; 201 | } 202 | 203 | public void setSessionId(String sessionId) { 204 | this.sessionId = sessionId; 205 | } 206 | 207 | public String getEntireFileSha() { 208 | return entireFileSha; 209 | } 210 | 211 | public void setEntireFileSha(String entireFileSha) { 212 | this.entireFileSha = entireFileSha; 213 | } 214 | 215 | public boolean isSerialUpload() { 216 | return serialUpload; 217 | } 218 | 219 | public void setSerialUpload(boolean serialUpload) { 220 | this.serialUpload = serialUpload; 221 | } 222 | 223 | public boolean isUploadFromBuffer() { 224 | return uploadFromBuffer; 225 | } 226 | 227 | public void setUploadFromBuffer(boolean uploadFromBuffer) { 228 | this.uploadFromBuffer = uploadFromBuffer; 229 | } 230 | 231 | public byte[] getContentBuffer() { 232 | return contentBuffer; 233 | } 234 | 235 | public void setContentBuffer(byte[] contentBuffer) { 236 | this.contentBuffer = contentBuffer; 237 | } 238 | 239 | } 240 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/op/BaseOp.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.op; 2 | 3 | import com.qcloud.cos.ClientConfig; 4 | import com.qcloud.cos.common_utils.CommonPathUtils; 5 | import com.qcloud.cos.exception.AbstractCosException; 6 | import com.qcloud.cos.http.AbstractCosHttpClient; 7 | import com.qcloud.cos.http.HttpContentType; 8 | import com.qcloud.cos.http.HttpMethod; 9 | import com.qcloud.cos.http.HttpRequest; 10 | import com.qcloud.cos.http.RequestBodyKey; 11 | import com.qcloud.cos.http.RequestBodyValue; 12 | import com.qcloud.cos.http.RequestHeaderKey; 13 | import com.qcloud.cos.http.RequestHeaderValue; 14 | import com.qcloud.cos.request.AbstractBaseRequest; 15 | import com.qcloud.cos.request.AbstractDelRequest; 16 | import com.qcloud.cos.request.AbstractStatRequest; 17 | import com.qcloud.cos.sign.Credentials; 18 | import com.qcloud.cos.sign.Sign; 19 | 20 | /** 21 | * @author chengwu 封装目录和文件共同的操作 22 | */ 23 | public abstract class BaseOp { 24 | // 配置信息 25 | protected ClientConfig config; 26 | // 鉴权信息 27 | protected Credentials cred; 28 | // http请求发送对象 29 | protected AbstractCosHttpClient httpClient; 30 | 31 | public BaseOp(ClientConfig config, Credentials cred, AbstractCosHttpClient httpClient) { 32 | super(); 33 | this.config = config; 34 | this.cred = cred; 35 | this.httpClient = httpClient; 36 | } 37 | 38 | public void setConfig(ClientConfig config) { 39 | this.config = config; 40 | } 41 | 42 | public void setCred(Credentials cred) { 43 | this.cred = cred; 44 | } 45 | 46 | public void setHttpClient(AbstractCosHttpClient httpClient) { 47 | this.httpClient = httpClient; 48 | } 49 | 50 | /** 51 | * 根据APPID, BUCKET, COS_PATH生成经过URL编码的URL 52 | * 53 | * @param request 基本类型的请求 54 | * @return URL字符串 55 | * @throws AbstractCosException 56 | */ 57 | protected String buildUrl(AbstractBaseRequest request) throws AbstractCosException { 58 | String endPoint = new StringBuilder().append(this.config.getUploadCosEndPointPrefix()) 59 | .append(this.config.getUploadCosEndPointDomain()) 60 | .append(this.config.getUploadCosEndPointSuffix()).toString(); 61 | long appId = this.cred.getAppId(); 62 | String bucketName = request.getBucketName(); 63 | String cosPath = request.getCosPath(); 64 | cosPath = CommonPathUtils.encodeRemotePath(cosPath); 65 | return String.format("%s/%d/%s%s", endPoint, appId, bucketName, cosPath); 66 | } 67 | 68 | /** 69 | * 删除文件或者目录 70 | * 71 | * @param request 删除文件或者目录的请求, 类型为DelFileRequest或者DelFolderRequest 72 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 73 | * message为success或者失败原因 74 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 75 | */ 76 | protected String delBase(final AbstractDelRequest request) throws AbstractCosException { 77 | request.check_param(); 78 | 79 | String url = buildUrl(request); 80 | String sign = 81 | Sign.getOneEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred); 82 | 83 | HttpRequest httpRequest = new HttpRequest(); 84 | httpRequest.setUrl(url); 85 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 86 | httpRequest.addHeader(RequestHeaderKey.Content_TYPE, RequestHeaderValue.ContentType.JSON); 87 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 88 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.DELETE); 89 | httpRequest.setMethod(HttpMethod.POST); 90 | httpRequest.setContentType(HttpContentType.APPLICATION_JSON); 91 | return httpClient.sendHttpRequest(httpRequest); 92 | } 93 | 94 | /** 95 | * 获取文件或者目录的属性 96 | * 97 | * @param request 文件或者目录的属性请求, 类型为StatFileRequest或者StatFolderRequest 98 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 99 | * message为success或者失败原因 100 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 101 | */ 102 | protected String statBase(final AbstractStatRequest request) throws AbstractCosException { 103 | request.check_param(); 104 | 105 | String url = buildUrl(request); 106 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 107 | String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), 108 | this.cred, signExpired); 109 | 110 | HttpRequest httpRequest = new HttpRequest(); 111 | httpRequest.setUrl(url); 112 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 113 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 114 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.STAT); 115 | httpRequest.setMethod(HttpMethod.GET); 116 | 117 | return httpClient.sendHttpRequest(httpRequest); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/op/FileOp.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.op; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.File; 6 | import java.io.FileNotFoundException; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.OutputStream; 11 | import java.nio.charset.Charset; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.Future; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | import org.json.JSONArray; 20 | import org.json.JSONObject; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import com.qcloud.cos.ClientConfig; 25 | import com.qcloud.cos.common_utils.CommonCodecUtils; 26 | import com.qcloud.cos.common_utils.CommonFileUtils; 27 | import com.qcloud.cos.common_utils.CommonPathUtils; 28 | import com.qcloud.cos.exception.AbstractCosException; 29 | import com.qcloud.cos.exception.ParamException; 30 | import com.qcloud.cos.exception.UnknownException; 31 | import com.qcloud.cos.http.AbstractCosHttpClient; 32 | import com.qcloud.cos.http.HttpContentType; 33 | import com.qcloud.cos.http.HttpMethod; 34 | import com.qcloud.cos.http.HttpRequest; 35 | import com.qcloud.cos.http.RequestBodyKey; 36 | import com.qcloud.cos.http.RequestBodyValue; 37 | import com.qcloud.cos.http.RequestHeaderKey; 38 | import com.qcloud.cos.http.RequestHeaderValue; 39 | import com.qcloud.cos.http.ResponseBodyKey; 40 | import com.qcloud.cos.meta.InsertOnly; 41 | import com.qcloud.cos.meta.SliceFileDataTask; 42 | import com.qcloud.cos.meta.UploadSliceFileContext; 43 | import com.qcloud.cos.request.DelFileRequest; 44 | import com.qcloud.cos.request.GetFileInputStreamRequest; 45 | import com.qcloud.cos.request.GetFileLocalRequest; 46 | import com.qcloud.cos.request.ListPartsRequest; 47 | import com.qcloud.cos.request.MoveFileRequest; 48 | import com.qcloud.cos.request.StatFileRequest; 49 | import com.qcloud.cos.request.UpdateFileRequest; 50 | import com.qcloud.cos.request.UploadFileRequest; 51 | import com.qcloud.cos.request.UploadSliceFileRequest; 52 | import com.qcloud.cos.sign.Credentials; 53 | import com.qcloud.cos.sign.Sign; 54 | 55 | /** 56 | * @author chengwu 此类封装了文件操作 57 | */ 58 | public class FileOp extends BaseOp { 59 | 60 | private static final Logger LOG = LoggerFactory.getLogger(FileOp.class); 61 | 62 | public FileOp(ClientConfig config, Credentials cred, AbstractCosHttpClient client) { 63 | super(config, cred, client); 64 | } 65 | 66 | private String buildGetFileUrl(GetFileInputStreamRequest request) throws AbstractCosException { 67 | StringBuilder strBuilder = new StringBuilder(); 68 | strBuilder.append(this.config.getDownCosEndPointPrefix()).append(request.getBucketName()) 69 | .append("-").append(this.cred.getAppId()).append("."); 70 | if (request.isUseCDN()) { 71 | strBuilder.append("file.myqcloud.com"); 72 | } else { 73 | strBuilder.append(this.config.getDownCosEndPointDomain()); 74 | } 75 | strBuilder.append(CommonPathUtils.encodeRemotePath(request.getCosPath())); 76 | String url = strBuilder.toString(); 77 | return url; 78 | } 79 | 80 | /** 81 | * 更新文件属性请求 82 | * 83 | * @param request 更新文件属性请求 84 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 85 | * message为success或者失败原因 86 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 87 | */ 88 | public String updateFile(final UpdateFileRequest request) throws AbstractCosException { 89 | request.check_param(); 90 | 91 | String url = buildUrl(request); 92 | String sign = 93 | Sign.getOneEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred); 94 | 95 | HttpRequest httpRequest = new HttpRequest(); 96 | httpRequest.setUrl(url); 97 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 98 | httpRequest.addHeader(RequestHeaderKey.Content_TYPE, RequestHeaderValue.ContentType.JSON); 99 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 100 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPDATE); 101 | int updateFlag = request.getUpdateFlag(); 102 | if ((updateFlag & 0x01) != 0) { 103 | httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr()); 104 | } 105 | if ((updateFlag & 0x40) != 0) { 106 | String customHeaderStr = new JSONObject(request.getCustomHeaders()).toString(); 107 | httpRequest.addParam(RequestBodyKey.CUSTOM_HEADERS, customHeaderStr); 108 | } 109 | if ((updateFlag & 0x80) != 0) { 110 | httpRequest.addParam(RequestBodyKey.AUTHORITY, request.getAuthority().toString()); 111 | } 112 | httpRequest.setMethod(HttpMethod.POST); 113 | httpRequest.setContentType(HttpContentType.APPLICATION_JSON); 114 | return httpClient.sendHttpRequest(httpRequest); 115 | } 116 | 117 | /** 118 | * 删除文件请求 119 | * 120 | * @param request 删除文件请求 121 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 122 | * message为success或者失败原因 123 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 124 | */ 125 | public String delFile(DelFileRequest request) throws AbstractCosException { 126 | return super.delBase(request); 127 | } 128 | 129 | /** 130 | * 获取文件属性请求 131 | * 132 | * @param request 获取文件属性请求 133 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 134 | * message为success或者失败原因 135 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 136 | */ 137 | public String statFile(StatFileRequest request) throws AbstractCosException { 138 | return super.statBase(request); 139 | } 140 | 141 | /** 142 | * 上传文件请求, 对小文件(8MB以下使用单文件上传接口), 大文件使用分片上传接口 143 | * 144 | * @param request 上传文件请求 145 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 146 | * message为success或者失败原因 147 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 148 | */ 149 | public String uploadFile(UploadFileRequest request) throws AbstractCosException { 150 | request.check_param(); 151 | 152 | String localPath = request.getLocalPath(); 153 | long fileSize = 0; 154 | if (request.isUploadFromBuffer()) { 155 | fileSize = request.getContentBufer().length; 156 | } else { 157 | try { 158 | fileSize = CommonFileUtils.getFileLength(localPath); 159 | } catch (Exception e) { 160 | throw new UnknownException(e.toString()); 161 | } 162 | } 163 | 164 | long suitSingleFileSize = 8 * 1024 * 1024; 165 | if (fileSize < suitSingleFileSize) { 166 | return uploadSingleFile(request); 167 | } else { 168 | UploadSliceFileRequest sliceRequest = new UploadSliceFileRequest(request); 169 | sliceRequest.setInsertOnly(request.getInsertOnly()); 170 | if (request.isUploadFromBuffer()) { 171 | sliceRequest.setContentBufer(request.getContentBufer()); 172 | } 173 | sliceRequest.setEnableShaDigest(request.isEnableShaDigest()); 174 | sliceRequest.setTaskNum(request.getTaskNum()); 175 | return uploadSliceFile(sliceRequest); 176 | } 177 | } 178 | 179 | /** 180 | * 上传单文件请求, 不分片 181 | * 182 | * @param request 上传文件请求 183 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 184 | * message为success或者失败原因 185 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 186 | */ 187 | public String uploadSingleFile(UploadFileRequest request) throws AbstractCosException { 188 | request.check_param(); 189 | 190 | String localPath = request.getLocalPath(); 191 | long fileSize = 0; 192 | if (request.isUploadFromBuffer()) { 193 | fileSize = request.getContentBufer().length; 194 | } else { 195 | try { 196 | fileSize = CommonFileUtils.getFileLength(localPath); 197 | } catch (Exception e) { 198 | throw new UnknownException(e.toString()); 199 | } 200 | } 201 | // 单文件上传上限不超过20MB 202 | if (fileSize > 20 * 1024 * 1024) { 203 | throw new ParamException("file is to big, please use uploadFile interface!"); 204 | } 205 | 206 | String fileContent = ""; 207 | String shaDigest = ""; 208 | try { 209 | if (request.isUploadFromBuffer()) { 210 | fileContent = new String(request.getContentBufer(), Charset.forName("ISO-8859-1")); 211 | shaDigest = CommonCodecUtils.getBufferSha1(request.getContentBufer()); 212 | } else { 213 | fileContent = CommonFileUtils.getFileContent(localPath); 214 | shaDigest = CommonCodecUtils.getEntireFileSha1(localPath); 215 | } 216 | } catch (Exception e) { 217 | throw new UnknownException(e.toString()); 218 | } 219 | 220 | String url = buildUrl(request); 221 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 222 | String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), 223 | this.cred, signExpired); 224 | 225 | HttpRequest httpRequest = new HttpRequest(); 226 | httpRequest.setUrl(url); 227 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 228 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 229 | 230 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD); 231 | httpRequest.addParam(RequestBodyKey.SHA, shaDigest); 232 | httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr()); 233 | httpRequest.addParam(RequestBodyKey.FILE_CONTENT, fileContent); 234 | httpRequest.addParam(RequestBodyKey.INSERT_ONLY, 235 | String.valueOf(request.getInsertOnly().ordinal())); 236 | 237 | httpRequest.setMethod(HttpMethod.POST); 238 | httpRequest.setContentType(HttpContentType.MULTIPART_FORM_DATA); 239 | 240 | String retStr = httpClient.sendHttpRequest(httpRequest); 241 | if (request.getInsertOnly() != InsertOnly.OVER_WRITE) { 242 | return retStr; 243 | } 244 | // 对于Overwrite类型,覆盖上传失败,做特殊处理,删掉重新传 245 | JSONObject retJson = new JSONObject(retStr); 246 | if (retJson.getInt("code") == 0) { 247 | return retStr; 248 | } 249 | // 1. Delete 250 | DelFileRequest del_request = 251 | new DelFileRequest(request.getBucketName(), request.getCosPath()); 252 | String delRet = delFile(del_request); 253 | JSONObject delJson = new JSONObject(delRet); 254 | if (delJson.getInt("code") != 0) { 255 | return retStr; 256 | } 257 | // 2. Upload Again 258 | return httpClient.sendHttpRequest(httpRequest); 259 | } 260 | 261 | /** 262 | * 分片上传文件 263 | * 264 | * @param request 分片上传请求 265 | * @return 服务器端返回的操作结果,成员code为0表示成功,具体参照文档手册 266 | * @throws Exception 267 | */ 268 | public String uploadSliceFile(UploadSliceFileRequest request) throws AbstractCosException { 269 | request.check_param(); 270 | UploadSliceFileContext context = new UploadSliceFileContext(request); 271 | context.setUrl(buildUrl(request)); 272 | String retStr = uploadFileWithCheckPoint(context); 273 | 274 | if (request.getInsertOnly() != InsertOnly.OVER_WRITE) { 275 | return retStr; 276 | } 277 | // 对于Overwrite类型,覆盖上传失败,做特殊处理,删掉重新传 278 | JSONObject retJson = new JSONObject(retStr); 279 | if (retJson.getInt("code") == 0) { 280 | return retStr; 281 | } 282 | // 1. Delete 283 | DelFileRequest del_request = 284 | new DelFileRequest(request.getBucketName(), request.getCosPath()); 285 | String delRet = delFile(del_request); 286 | JSONObject delJson = new JSONObject(delRet); 287 | if (delJson.getInt("code") != 0) { 288 | return retStr; 289 | } 290 | // 2. Upload Again 291 | retStr = uploadFileWithCheckPoint(context); 292 | retJson = new JSONObject(retStr); 293 | if (retJson.getInt("code") != 0) { 294 | del_request = new DelFileRequest(request.getBucketName(), request.getCosPath()); 295 | delFile(del_request); 296 | } 297 | return retStr; 298 | } 299 | 300 | /** 301 | * 移动文件请求(重命名) 302 | * 303 | * @param request 移动文件请求 304 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 其他为失败, 305 | * message为success或者失败原因 306 | * @throws AbstractCosException SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 307 | */ 308 | public String moveFile(MoveFileRequest request) throws AbstractCosException { 309 | request.check_param(); 310 | 311 | String url = buildUrl(request); 312 | String sign = 313 | Sign.getOneEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred); 314 | 315 | HttpRequest httpRequest = new HttpRequest(); 316 | httpRequest.setUrl(url); 317 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 318 | httpRequest.addHeader(RequestHeaderKey.Content_TYPE, RequestHeaderValue.ContentType.JSON); 319 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 320 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.MOVE); 321 | httpRequest.addParam(RequestBodyKey.DEST_FIELD, request.getDstCosPath()); 322 | httpRequest.addParam(RequestBodyKey.TO_OVER_WRITE, 323 | String.valueOf(request.getOverWrite().ordinal())); 324 | httpRequest.setMethod(HttpMethod.POST); 325 | httpRequest.setContentType(HttpContentType.APPLICATION_JSON); 326 | return httpClient.sendHttpRequest(httpRequest); 327 | } 328 | 329 | // 断点续传 330 | private String uploadFileWithCheckPoint(UploadSliceFileContext context) 331 | throws AbstractCosException { 332 | JSONObject initResult = sendSliceInit(context); 333 | 334 | int initResultCode = initResult.getInt(ResponseBodyKey.CODE); 335 | // 4019文件未上传完成,这时候应该用断点续传 336 | if (initResultCode != 0 && initResultCode != -4019) { 337 | return initResult.toString(); 338 | } 339 | 340 | if (initResultCode == 0) { 341 | JSONObject data = initResult.getJSONObject(ResponseBodyKey.DATA); 342 | if (data.has(ResponseBodyKey.Data.ACCESS_URL)) { 343 | return initResult.toString(); 344 | } 345 | if (data.has(ResponseBodyKey.Data.SERIAL_UPLOAD) 346 | && data.getInt(ResponseBodyKey.Data.SERIAL_UPLOAD) == 1) { 347 | LOG.debug("SERIAL_UPLOAD is true"); 348 | context.setSerialUpload(true); 349 | } else { 350 | LOG.debug("SERIAL_UPLOAD is false");; 351 | context.setSerialUpload(false); 352 | } 353 | context.setSessionId(data.getString(ResponseBodyKey.Data.SESSION)); 354 | // 如果服务端返回的slice_slize和用户要求的不一致, 则重新分片 355 | if (data.getInt(ResponseBodyKey.Data.SLICE_SIZE) != context.getSliceSize()) { 356 | context.setSliceSize(data.getInt(ResponseBodyKey.Data.SLICE_SIZE)); 357 | } 358 | } else { 359 | ListPartsRequest listPartsRequest = 360 | new ListPartsRequest(context.getBucketName(), context.getCosPath()); 361 | String listPartsResult = sliceListParts(listPartsRequest); 362 | JSONObject listPartsJson = new JSONObject(listPartsResult); 363 | if (listPartsJson.getInt(ResponseBodyKey.CODE) != 0) { 364 | return listPartsJson.toString(); 365 | } 366 | 367 | JSONObject data = listPartsJson.getJSONObject(ResponseBodyKey.DATA); 368 | if (data.has(ResponseBodyKey.Data.LISTPARTS)) { 369 | JSONArray listPartsJsonArry = data.getJSONArray(ResponseBodyKey.Data.LISTPARTS); 370 | context.setUploadCompleteParts(listPartsJsonArry); 371 | } 372 | context.setSessionId(data.getString(ResponseBodyKey.Data.SESSION)); 373 | } 374 | 375 | context.prepareUploadPartsInfo(); 376 | // 并行发送数据分片 377 | JSONObject sendParallelRet = sendSliceDataParallel(context); 378 | if (sendParallelRet.getInt(ResponseBodyKey.CODE) != 0) { 379 | return sendParallelRet.toString(); 380 | } 381 | 382 | // 发送finish分片 383 | JSONObject finishRet = sendSliceFinish(context); 384 | return finishRet.toString(); 385 | } 386 | 387 | /** 388 | * 分片上传第一步,发送init分片 389 | * 390 | * @param context 分片上传请求上下文 391 | * @return 服务器端返回的操作结果,code为0表示成功,其他包括sessionId、offset等,具体参见文档手册 392 | * @throws Exception 393 | */ 394 | private JSONObject sendSliceInit(UploadSliceFileContext context) throws AbstractCosException { 395 | String localPath = context.getLocalPath(); 396 | long fileSize = context.getFileSize(); 397 | int sliceSize = context.getSliceSize(); 398 | 399 | StringBuilder entireDigestSb = new StringBuilder(); 400 | String slicePartDigest = ""; 401 | try { 402 | if (context.isEnableShaDigest()) { 403 | if (context.isUploadFromBuffer()) { 404 | slicePartDigest = CommonCodecUtils.getSlicePartSha1(context.getContentBuffer(), 405 | sliceSize, entireDigestSb); 406 | } else { 407 | slicePartDigest = 408 | CommonCodecUtils.getSlicePartSha1(localPath, sliceSize, entireDigestSb); 409 | } 410 | context.setEntireFileSha(entireDigestSb.toString()); 411 | LOG.debug("slicePartDigest: " + slicePartDigest); 412 | } 413 | } catch (Exception e) { 414 | throw new UnknownException(e.getMessage()); 415 | } 416 | 417 | String url = context.getUrl(); 418 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 419 | String sign = Sign.getPeriodEffectiveSign(context.getBucketName(), context.getCosPath(), 420 | this.cred, signExpired); 421 | 422 | HttpRequest httpRequest = new HttpRequest(); 423 | httpRequest.setUrl(url); 424 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 425 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 426 | httpRequest.addParam(RequestBodyKey.FILE_SIZE, String.valueOf(fileSize)); 427 | httpRequest.addParam(RequestBodyKey.SLICE_SIZE, String.valueOf(sliceSize)); 428 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD_SLICE_INIT); 429 | httpRequest.addParam(RequestBodyKey.INSERT_ONLY, 430 | String.valueOf(context.getInsertOnly().ordinal())); 431 | httpRequest.addParam(RequestBodyKey.BIZ_ATTR, context.getBizAttr()); 432 | if (context.isEnableShaDigest()) { 433 | httpRequest.addParam(RequestBodyKey.SHA, entireDigestSb.toString()); 434 | httpRequest.addParam(RequestBodyKey.UPLOAD_PARTS, slicePartDigest); 435 | } 436 | 437 | httpRequest.setMethod(HttpMethod.POST); 438 | httpRequest.setContentType(HttpContentType.MULTIPART_FORM_DATA); 439 | 440 | JSONObject resultJson = null; 441 | String resultStr = this.httpClient.sendHttpRequest(httpRequest); 442 | LOG.debug("sendSliceInit, resultStr: " + resultStr); 443 | resultJson = new JSONObject(resultStr); 444 | return resultJson; 445 | } 446 | 447 | /** 448 | * 分片上传第二步,发送数据分片 449 | * 450 | * @param context 分片上传请求 451 | * @return 服务器端返回的操作结果,code为0表示成功 452 | * @throws Exception 453 | */ 454 | private JSONObject sendSliceDataParallel(UploadSliceFileContext context) 455 | throws AbstractCosException { 456 | List> allSliceTasks = new ArrayList>(); 457 | // 默认串行执行,只用一个线程,如果server端支持并行上传,则用多个线程执行 458 | int threadNum = 1; 459 | if (!context.isSerialUpload()) { 460 | threadNum = context.getTaskNum(); 461 | } 462 | ExecutorService service = Executors.newFixedThreadPool(threadNum); 463 | 464 | String url = context.getUrl(); 465 | long signExpired = this.config.getSignExpired(); 466 | for (int sliceIndex = 0; sliceIndex < context.sliceParts.size(); ++sliceIndex) { 467 | if (!context.sliceParts.get(sliceIndex).isUploadCompleted()) { 468 | SliceFileDataTask dataTask = new SliceFileDataTask(sliceIndex, sliceIndex, context, 469 | httpClient, cred, url, signExpired); 470 | allSliceTasks.add(service.submit(dataTask)); 471 | } 472 | } 473 | service.shutdown(); 474 | 475 | try { 476 | service.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); 477 | service.shutdownNow(); 478 | } catch (Exception e) { 479 | throw new UnknownException(e.getMessage()); 480 | } 481 | 482 | JSONObject taskResult = null; 483 | if (allSliceTasks.size() == 0) { 484 | taskResult = new JSONObject(); 485 | taskResult.put(ResponseBodyKey.CODE, 0); 486 | return taskResult; 487 | } 488 | 489 | for (Future task : allSliceTasks) { 490 | try { 491 | taskResult = task.get(); 492 | } catch (Exception e) { 493 | throw new UnknownException(e.getMessage()); 494 | } 495 | if (taskResult.getInt(ResponseBodyKey.CODE) != 0) { 496 | return taskResult; 497 | } 498 | } 499 | 500 | return taskResult; 501 | } 502 | 503 | /** 504 | * 最后一步, 发送finish分片 505 | * 506 | * @return 服务器端返回的操作结果,成功code为0 507 | * @throws Exception 508 | */ 509 | private JSONObject sendSliceFinish(UploadSliceFileContext context) throws AbstractCosException { 510 | String url = context.getUrl(); 511 | 512 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 513 | String sign = Sign.getPeriodEffectiveSign(context.getBucketName(), context.getCosPath(), 514 | this.cred, signExpired); 515 | 516 | HttpRequest httpRequest = new HttpRequest(); 517 | httpRequest.setUrl(url); 518 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 519 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 520 | httpRequest.addParam(RequestBodyKey.SESSION, context.getSessionId()); 521 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD_SLICE_FINISH); 522 | if (context.isEnableShaDigest()) { 523 | httpRequest.addParam(RequestBodyKey.SHA, context.getEntireFileSha()); 524 | } 525 | httpRequest.addParam(RequestBodyKey.FILE_SIZE, String.valueOf(context.getFileSize())); 526 | 527 | httpRequest.setContentType(HttpContentType.MULTIPART_FORM_DATA); 528 | httpRequest.setMethod(HttpMethod.POST); 529 | 530 | JSONObject resultJson = null; 531 | String resultStr = this.httpClient.sendHttpRequest(httpRequest); 532 | resultJson = new JSONObject(resultStr); 533 | LOG.debug("sendSliceFinish, resultStr: " + resultStr); 534 | return resultJson; 535 | } 536 | 537 | public String sliceListParts(ListPartsRequest request) throws AbstractCosException { 538 | request.check_param(); 539 | 540 | String url = buildUrl(request); 541 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 542 | String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), 543 | this.cred, signExpired); 544 | 545 | HttpRequest httpRequest = new HttpRequest(); 546 | httpRequest.setUrl(url); 547 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 548 | httpRequest.addHeader(RequestHeaderKey.Content_TYPE, RequestHeaderValue.ContentType.JSON); 549 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 550 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPLOAD_SLICE_LIST); 551 | httpRequest.setMethod(HttpMethod.POST); 552 | httpRequest.setContentType(HttpContentType.APPLICATION_JSON); 553 | return httpClient.sendHttpRequest(httpRequest); 554 | } 555 | 556 | public String getFileLocal(GetFileLocalRequest request) throws AbstractCosException { 557 | InputStream in = getFileInputStream(request); 558 | BufferedInputStream bis = new BufferedInputStream(in); 559 | OutputStream out = null; 560 | try { 561 | out = new FileOutputStream(new File(request.getLocalPath())); 562 | } catch (FileNotFoundException e) { 563 | throw new UnknownException(e.getMessage()); 564 | } 565 | BufferedOutputStream bos = new BufferedOutputStream(out); 566 | int inByte; 567 | try { 568 | while ((inByte = bis.read()) != -1) 569 | bos.write(inByte); 570 | } catch (IOException e) { 571 | throw new UnknownException(e.getMessage()); 572 | } finally { 573 | try { 574 | bis.close(); 575 | bos.close(); 576 | } catch (IOException e) { 577 | throw new UnknownException(e.getMessage()); 578 | } 579 | } 580 | JSONObject retJson = new JSONObject(); 581 | retJson.put(ResponseBodyKey.CODE, 0); 582 | retJson.put(ResponseBodyKey.MESSAGE, "SUCCESS"); 583 | return retJson.toString(); 584 | } 585 | 586 | public InputStream getFileInputStream(GetFileInputStreamRequest request) 587 | throws AbstractCosException { 588 | String url = buildGetFileUrl(request); 589 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 590 | String sign = Sign.getDownLoadSign(request.getBucketName(), request.getCosPath(), this.cred, 591 | signExpired); 592 | 593 | StringBuilder rangeBuilder = new StringBuilder(); 594 | if (request.getRangeStart() != 0 || request.getRangeEnd() != Long.MAX_VALUE) { 595 | rangeBuilder.append("bytes=").append(request.getRangeStart()).append("-"); 596 | rangeBuilder.append(request.getRangeEnd()); 597 | } 598 | 599 | HttpRequest httpRequest = new HttpRequest(); 600 | httpRequest.setUrl(url); 601 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 602 | if (!rangeBuilder.toString().isEmpty()) { 603 | httpRequest.addHeader(RequestHeaderKey.RANGE, rangeBuilder.toString()); 604 | } 605 | if (!request.getReferer().isEmpty()) { 606 | httpRequest.addHeader(RequestHeaderKey.REFERER, request.getReferer()); 607 | } 608 | httpRequest.addParam(RequestHeaderKey.SIGN, sign); 609 | httpRequest.setMethod(HttpMethod.GET); 610 | return httpClient.getFileInputStream(httpRequest); 611 | } 612 | } 613 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/op/FolderOp.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.op; 2 | 3 | import com.qcloud.cos.ClientConfig; 4 | import com.qcloud.cos.exception.AbstractCosException; 5 | import com.qcloud.cos.http.AbstractCosHttpClient; 6 | import com.qcloud.cos.http.HttpContentType; 7 | import com.qcloud.cos.http.HttpMethod; 8 | import com.qcloud.cos.http.HttpRequest; 9 | import com.qcloud.cos.http.RequestBodyKey; 10 | import com.qcloud.cos.http.RequestBodyValue; 11 | import com.qcloud.cos.http.RequestHeaderKey; 12 | import com.qcloud.cos.http.RequestHeaderValue; 13 | import com.qcloud.cos.request.CreateFolderRequest; 14 | import com.qcloud.cos.request.DelFolderRequest; 15 | import com.qcloud.cos.request.ListFolderRequest; 16 | import com.qcloud.cos.request.StatFolderRequest; 17 | import com.qcloud.cos.request.UpdateFolderRequest; 18 | import com.qcloud.cos.sign.Credentials; 19 | import com.qcloud.cos.sign.Sign; 20 | 21 | /** 22 | * @author chengwu 23 | * 此类封装了常用的目录操作 24 | */ 25 | public class FolderOp extends BaseOp { 26 | 27 | public FolderOp(ClientConfig config, Credentials cred, AbstractCosHttpClient client) { 28 | super(config, cred, client); 29 | } 30 | 31 | /** 32 | * 更新目录属性请求 33 | * 34 | * @param request 35 | * 更新目录属性请求 36 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 37 | * 其他为失败, message为success或者失败原因 38 | * @throws AbstractCosException 39 | * SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 40 | */ 41 | public String updateFolder(UpdateFolderRequest request) throws AbstractCosException { 42 | request.check_param(); 43 | 44 | String url = buildUrl(request); 45 | String sign = Sign.getOneEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred); 46 | 47 | HttpRequest httpRequest = new HttpRequest(); 48 | httpRequest.setUrl(url); 49 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 50 | httpRequest.addHeader(RequestHeaderKey.Content_TYPE, RequestHeaderValue.ContentType.JSON); 51 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 52 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.UPDATE); 53 | httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr()); 54 | 55 | httpRequest.setMethod(HttpMethod.POST); 56 | httpRequest.setContentType(HttpContentType.APPLICATION_JSON); 57 | return httpClient.sendHttpRequest(httpRequest); 58 | } 59 | 60 | /** 61 | * 删除目录请求 62 | * 63 | * @param request 64 | * 删除目录请求 65 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 66 | * 其他为失败, message为success或者失败原因 67 | * @throws AbstractCosException 68 | * SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 69 | */ 70 | public String delFolder(DelFolderRequest request) throws AbstractCosException { 71 | return super.delBase(request); 72 | } 73 | 74 | /** 75 | * 获取目录属性请求 76 | * 77 | * @param request 78 | * 获取目录属性请求 79 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 80 | * 其他为失败, message为success或者失败原因 81 | * @throws AbstractCosException 82 | * SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 83 | */ 84 | public String statFolder(StatFolderRequest request) throws AbstractCosException { 85 | return super.statBase(request); 86 | } 87 | 88 | /** 89 | * 创建目录请求 90 | * 91 | * @param request 92 | * 创建目录属性请求 93 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 94 | * 其他为失败, message为success或者失败原因 95 | * @throws AbstractCosException 96 | * SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 97 | */ 98 | public String createFolder(CreateFolderRequest request) throws AbstractCosException { 99 | request.check_param(); 100 | 101 | String url = buildUrl(request); 102 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 103 | String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired); 104 | 105 | HttpRequest httpRequest = new HttpRequest(); 106 | httpRequest.setUrl(url); 107 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 108 | httpRequest.addHeader(RequestHeaderKey.Content_TYPE, RequestHeaderValue.ContentType.JSON); 109 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 110 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.CREATE); 111 | httpRequest.addParam(RequestBodyKey.BIZ_ATTR, request.getBizAttr()); 112 | 113 | httpRequest.setMethod(HttpMethod.POST); 114 | httpRequest.setContentType(HttpContentType.APPLICATION_JSON); 115 | return httpClient.sendHttpRequest(httpRequest); 116 | } 117 | 118 | /** 119 | * 获取目录列表请求 120 | * 121 | * @param request 122 | * list目录属性请求 123 | * @return JSON格式的字符串, 格式为{"code":$code, "message":"$mess"}, code为0表示成功, 124 | * 其他为失败, message为success或者失败原因 125 | * @throws AbstractCosException 126 | * SDK定义的COS异常, 通常是输入参数有误或者环境问题(如网络不通) 127 | */ 128 | public String listFolder(ListFolderRequest request) throws AbstractCosException { 129 | 130 | request.check_param(); 131 | request.setCosPath(request.getCosPath() + request.getPrefix()); 132 | 133 | String url = buildUrl(request); 134 | long signExpired = System.currentTimeMillis() / 1000 + this.config.getSignExpired(); 135 | String sign = Sign.getPeriodEffectiveSign(request.getBucketName(), request.getCosPath(), this.cred, signExpired); 136 | 137 | HttpRequest httpRequest = new HttpRequest(); 138 | httpRequest.setUrl(url); 139 | httpRequest.addHeader(RequestHeaderKey.Authorization, sign); 140 | httpRequest.addHeader(RequestHeaderKey.USER_AGENT, this.config.getUserAgent()); 141 | httpRequest.addParam(RequestBodyKey.OP, RequestBodyValue.OP.LIST); 142 | httpRequest.addParam(RequestBodyKey.NUM, String.valueOf(request.getNum())); 143 | httpRequest.addParam(RequestBodyKey.LIST_FLAG, String.valueOf(request.getListFlag())); 144 | httpRequest.addParam(RequestBodyKey.CONTEXT, request.getContext()); 145 | httpRequest.addParam(RequestBodyKey.DELIMITER, request.getDelimiter()); 146 | httpRequest.setMethod(HttpMethod.GET); 147 | 148 | return httpClient.sendHttpRequest(httpRequest); 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/AbstractBaseRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | /** 6 | * @author chengwu 7 | * 封装了请求包含的基本元素 8 | */ 9 | public abstract class AbstractBaseRequest { 10 | // bucket名 11 | private String bucketName; 12 | // cos路径 13 | private String cosPath; 14 | 15 | public AbstractBaseRequest(String bucketName, String cosPath) { 16 | super(); 17 | this.bucketName = bucketName; 18 | this.cosPath = cosPath; 19 | } 20 | 21 | // 获取bucket名 22 | public String getBucketName() { 23 | return bucketName; 24 | } 25 | 26 | // 设置bucket名 27 | public void setBucketName(String bucketName) { 28 | this.bucketName = bucketName; 29 | } 30 | 31 | // 获取cos_path 32 | public String getCosPath() { 33 | return cosPath; 34 | } 35 | 36 | // 设置cos_path 37 | public void setCosPath(String cosPath) { 38 | this.cosPath = cosPath; 39 | } 40 | 41 | protected String getMemberStringValue(String member) { 42 | if (member == null) { 43 | return "null"; 44 | } else { 45 | return member; 46 | } 47 | } 48 | 49 | // 将request转换为字符串, 用于记录信息 50 | @Override 51 | public String toString() { 52 | StringBuilder sb = new StringBuilder(); 53 | sb.append("bucketName:").append(getMemberStringValue(bucketName)); 54 | sb.append(", cosPath:").append(getMemberStringValue(cosPath)); 55 | return sb.toString(); 56 | } 57 | 58 | 59 | 60 | // 检查用户的输入参数 61 | public void check_param() throws ParamException { 62 | CommonParamCheckUtils.AssertNotNull("bucketName", this.bucketName); 63 | CommonParamCheckUtils.AssertNotNull("cosPath", this.cosPath); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/AbstractDelRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | /** 4 | * @author chengwu 5 | * 删除文件请求 6 | */ 7 | public class AbstractDelRequest extends AbstractBaseRequest { 8 | 9 | public AbstractDelRequest(String bucketName, String cosPath) { 10 | super(bucketName, cosPath); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/AbstractStatRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | /** 4 | * @author chengwu 5 | * 获取文件属性信息 6 | */ 7 | public class AbstractStatRequest extends AbstractBaseRequest { 8 | 9 | public AbstractStatRequest(String bucketName, String cosPath) { 10 | super(bucketName, cosPath); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/CreateFolderRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 8 | * 创建目录请求 9 | */ 10 | public class CreateFolderRequest extends AbstractBaseRequest { 11 | // 目录属性,默认为空 12 | private String bizAttr = ""; 13 | 14 | public CreateFolderRequest(String bucketName, String cosPath) { 15 | this(bucketName, cosPath, ""); 16 | } 17 | 18 | public CreateFolderRequest(String bucketName, String cosPath, String bizAttr) { 19 | super(bucketName, cosPath); 20 | this.bizAttr = bizAttr; 21 | } 22 | 23 | public void setBizAttr(String bizAttr) { 24 | this.bizAttr = bizAttr; 25 | } 26 | 27 | public String getBizAttr() { 28 | return bizAttr; 29 | } 30 | 31 | @Override 32 | public void check_param() throws ParamException { 33 | super.check_param(); 34 | CommonParamCheckUtils.AssertNotNull("bizAttr", this.bizAttr); 35 | CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath()); 36 | CommonParamCheckUtils.AssertNotRootCosPath(this.getCosPath()); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | StringBuilder sb = new StringBuilder(); 42 | sb.append(super.toString()); 43 | sb.append(", bizAttr:").append(getMemberStringValue(bizAttr)); 44 | return sb.toString(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/DelFileRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 8 | * 删除目录请求 9 | */ 10 | public class DelFileRequest extends AbstractDelRequest { 11 | 12 | public DelFileRequest(String bucketName, String cosPath) { 13 | super(bucketName, cosPath); 14 | } 15 | 16 | @Override 17 | public void check_param() throws ParamException { 18 | super.check_param(); 19 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/DelFolderRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 8 | * 删除文件请求 9 | */ 10 | public class DelFolderRequest extends AbstractDelRequest { 11 | 12 | public DelFolderRequest(String bucketName, String cosPath) { 13 | super(bucketName, cosPath); 14 | } 15 | 16 | @Override 17 | public void check_param() throws ParamException { 18 | super.check_param(); 19 | CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath()); 20 | CommonParamCheckUtils.AssertNotRootCosPath(this.getCosPath()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/GetFileInputStreamRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 获取下载文件的输入流 8 | */ 9 | public class GetFileInputStreamRequest extends AbstractBaseRequest { 10 | 11 | // 使用CDN加速下载,默认开启, 否则从COS源站进行下载 12 | private boolean useCDN = true; 13 | 14 | // referer设置, 如果没开启referer防盗链,可以不设置 15 | private String referer = ""; 16 | 17 | // 下载文件的range的起始位置 18 | private long rangeStart = 0; 19 | // 下载文件的range的结束位置 20 | private long rangeEnd = Long.MAX_VALUE; 21 | 22 | public GetFileInputStreamRequest(String bucketName, String cosPath) { 23 | super(bucketName, cosPath); 24 | } 25 | 26 | @Override 27 | public void check_param() throws ParamException { 28 | super.check_param(); 29 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 30 | } 31 | 32 | public long getRangeStart() { 33 | return rangeStart; 34 | } 35 | 36 | public void setRangeStart(long rangeStart) { 37 | this.rangeStart = rangeStart; 38 | } 39 | 40 | public long getRangeEnd() { 41 | return rangeEnd; 42 | } 43 | 44 | public void setRangeEnd(long rangeEnd) { 45 | this.rangeEnd = rangeEnd; 46 | } 47 | 48 | public boolean isUseCDN() { 49 | return useCDN; 50 | } 51 | 52 | public void setUseCDN(boolean useCDN) { 53 | this.useCDN = useCDN; 54 | } 55 | 56 | public String getReferer() { 57 | return referer; 58 | } 59 | 60 | public void setReferer(String referer) { 61 | this.referer = referer; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/GetFileLocalRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 下载文件到本地请求 8 | */ 9 | public class GetFileLocalRequest extends GetFileInputStreamRequest { 10 | 11 | // 要下载到的本地文件 12 | private String localPath = null; 13 | 14 | public GetFileLocalRequest(String bucketName, String cosPath, String localPath) { 15 | super(bucketName, cosPath); 16 | this.localPath = localPath; 17 | } 18 | 19 | public String getLocalPath() { 20 | return localPath; 21 | } 22 | 23 | public void setLocalPath(String localPath) { 24 | this.localPath = localPath; 25 | } 26 | 27 | 28 | @Override 29 | public void check_param() throws ParamException { 30 | super.check_param(); 31 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/ListFolderRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 获取目录成员请求 8 | */ 9 | public class ListFolderRequest extends AbstractBaseRequest { 10 | 11 | // 默认获取的最大目录成员数量 12 | private final int DEFAULT_LIST_NUM = 199; 13 | private final int DEFAULT_LIST_FLAG = 1; 14 | 15 | private int num = DEFAULT_LIST_NUM; 16 | private int listFlag = DEFAULT_LIST_FLAG; 17 | private String context = ""; 18 | private String prefix = ""; 19 | private String delimiter = "/"; 20 | 21 | public ListFolderRequest(String bucketName, String cosPath) { 22 | super(bucketName, cosPath); 23 | } 24 | 25 | public int getNum() { 26 | return num; 27 | } 28 | 29 | public void setNum(int num) { 30 | this.num = num; 31 | } 32 | 33 | public String getContext() { 34 | return context; 35 | } 36 | 37 | public void setContext(String context) { 38 | this.context = context; 39 | } 40 | 41 | public String getPrefix() { 42 | return prefix; 43 | } 44 | 45 | public void setPrefix(String prefix) { 46 | this.prefix = prefix; 47 | } 48 | 49 | public int getListFlag() { 50 | return listFlag; 51 | } 52 | 53 | public void setListFlag(int listFlag) { 54 | this.listFlag = listFlag; 55 | } 56 | 57 | public String getDelimiter() { 58 | return delimiter; 59 | } 60 | 61 | public void setDelimiter(String delimiter) { 62 | this.delimiter = delimiter; 63 | } 64 | 65 | @Override 66 | public void check_param() throws ParamException { 67 | super.check_param(); 68 | CommonParamCheckUtils.AssertLegalCosFolderPath(getCosPath()); 69 | CommonParamCheckUtils.AssertNotNull("context", this.context); 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | StringBuilder sb = new StringBuilder(); 75 | sb.append(super.toString()); 76 | sb.append(", num:").append(this.num); 77 | sb.append(", context:").append(getMemberStringValue(this.context)); 78 | sb.append(", listFlag:").append(this.listFlag); 79 | sb.append(", prefix:").append(getMemberStringValue(this.prefix)); 80 | return sb.toString(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/ListPartsRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | public class ListPartsRequest extends AbstractBaseRequest { 7 | 8 | public ListPartsRequest(String bucketName, String cosPath) { 9 | super(bucketName, cosPath); 10 | } 11 | 12 | @Override 13 | public void check_param() throws ParamException { 14 | super.check_param(); 15 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/MoveFileRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | import com.qcloud.cos.meta.OverWrite; 6 | 7 | public class MoveFileRequest extends AbstractBaseRequest { 8 | private String dstCosPath = ""; 9 | private OverWrite overWrite = OverWrite.NO_OVER_WRITE; 10 | 11 | public MoveFileRequest(String bucketName, String cosPath, String dstCosPath) { 12 | super(bucketName, cosPath); 13 | this.dstCosPath = dstCosPath; 14 | } 15 | 16 | public String getDstCosPath() { 17 | return this.dstCosPath; 18 | } 19 | 20 | public void setDstCosPath(String dstCosPath) { 21 | this.dstCosPath = dstCosPath; 22 | } 23 | 24 | public OverWrite getOverWrite() { 25 | return overWrite; 26 | } 27 | 28 | public void setOverWrite(OverWrite overWrite) { 29 | this.overWrite = overWrite; 30 | } 31 | 32 | @Override 33 | public void check_param() throws ParamException { 34 | super.check_param(); 35 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 36 | CommonParamCheckUtils.AssertLegalCosFilePath(this.dstCosPath); 37 | CommonParamCheckUtils.AssertNotNull("overWrite", this.overWrite); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/StatFileRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 8 | * 获取文件属性信息 9 | */ 10 | public class StatFileRequest extends AbstractStatRequest { 11 | 12 | public StatFileRequest(String bucketName, String cosPath) { 13 | super(bucketName, cosPath); 14 | } 15 | 16 | @Override 17 | public void check_param() throws ParamException { 18 | super.check_param(); 19 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/StatFolderRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 8 | * 获取目录属性信息 9 | */ 10 | public class StatFolderRequest extends AbstractStatRequest { 11 | 12 | public StatFolderRequest(String bucketName, String cosPath) { 13 | super(bucketName, cosPath); 14 | } 15 | 16 | @Override 17 | public void check_param() throws ParamException { 18 | super.check_param(); 19 | CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/UpdateFileRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 7 | import com.qcloud.cos.exception.ParamException; 8 | import com.qcloud.cos.meta.FileAuthority; 9 | 10 | /** 11 | * @author chengwu 更新文件请求 12 | * 13 | */ 14 | public class UpdateFileRequest extends AbstractBaseRequest { 15 | // 用户更新标识,更新bizAttr是0x01, 更新authority0x80, 更新custom_httpheader是0x40 16 | // 更新多个属性是这些这些值取或 17 | private int updatFlag = 0; 18 | 19 | // biz_attr属性 20 | private String bizAttr = ""; 21 | // 权限 22 | private FileAuthority authority = FileAuthority.INVALID; 23 | // HTTP Cache-Control属性 24 | private String cacheControl = ""; 25 | // HTTP Content-Type属性 26 | private String contentType = ""; 27 | // HTTP Content-Disposition属性 28 | private String contentDisposition = ""; 29 | // HTTP Content-Language属性 30 | private String contentLanguage = ""; 31 | // HTTP Content-Encoding属性 32 | private String contentEncoding = ""; 33 | // 自定义http头 34 | private Map customHeaders = new HashMap(); 35 | // 自定义http头, key为x-cos-meta-开头, value为字符串 36 | private Map xCosMetaHeaders = new HashMap(); 37 | 38 | public UpdateFileRequest(String bucketName, String cosPath) { 39 | super(bucketName, cosPath); 40 | } 41 | 42 | public int getUpdateFlag() { 43 | return updatFlag; 44 | } 45 | 46 | public String getBizAttr() { 47 | return bizAttr; 48 | } 49 | 50 | public void setBizAttr(String bizAttr) { 51 | this.bizAttr = bizAttr; 52 | this.updatFlag |= 0x01; 53 | } 54 | 55 | public FileAuthority getAuthority() { 56 | return authority; 57 | } 58 | 59 | public void setAuthority(FileAuthority authority) { 60 | this.authority = authority; 61 | this.updatFlag |= 0x80; 62 | } 63 | 64 | public Map getCustomHeaders() { 65 | return customHeaders; 66 | } 67 | 68 | public void setCacheControl(String cacheControl) { 69 | this.cacheControl = cacheControl; 70 | this.updatFlag |= 0x40; 71 | this.customHeaders.put("Cache-Control", cacheControl); 72 | } 73 | 74 | public void setContentType(String contentType) { 75 | this.contentType = contentType; 76 | this.updatFlag |= 0x40; 77 | this.customHeaders.put("Content-Type", contentType); 78 | } 79 | 80 | public void setContentDisposition(String contentDisposition) { 81 | this.contentDisposition = contentDisposition; 82 | this.updatFlag |= 0x40; 83 | this.customHeaders.put("Content-Disposition", contentDisposition); 84 | } 85 | 86 | public void setContentLanguage(String contentLanguage) { 87 | this.contentLanguage = contentLanguage; 88 | this.updatFlag |= 0x40; 89 | this.customHeaders.put("Content-Language", contentLanguage); 90 | } 91 | 92 | 93 | public void setContentEncoding(String contentEncoding) { 94 | this.contentEncoding = contentEncoding; 95 | this.updatFlag |= 0x40; 96 | this.customHeaders.put("Content-Encoding", contentEncoding); 97 | } 98 | 99 | public void setXCosMeta(String key, String value) { 100 | this.xCosMetaHeaders.put(key, value); 101 | this.customHeaders.put(key, value); 102 | this.updatFlag |= 0x40; 103 | } 104 | 105 | @Override 106 | public void check_param() throws ParamException { 107 | super.check_param(); 108 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 109 | CommonParamCheckUtils.AssertLegalUpdateFlag(this.updatFlag); 110 | CommonParamCheckUtils.AssertNotNull("biz_attr", this.bizAttr); 111 | CommonParamCheckUtils.AssertNotNull("authority", this.authority); 112 | CommonParamCheckUtils.AssertNotNull("cacheControl", this.cacheControl); 113 | CommonParamCheckUtils.AssertNotNull("contentType", this.contentType); 114 | CommonParamCheckUtils.AssertNotNull("contentDisposition", this.contentDisposition); 115 | CommonParamCheckUtils.AssertNotNull("contentLanguage", this.contentLanguage); 116 | CommonParamCheckUtils.AssertNotNull("contentEncoding", this.contentEncoding); 117 | CommonParamCheckUtils.AssertLegalXCosMeta(this.xCosMetaHeaders); 118 | } 119 | 120 | @Override 121 | public String toString() { 122 | StringBuilder sb = new StringBuilder(); 123 | sb.append(super.toString()); 124 | sb.append(", biz_attr:").append(getMemberStringValue(bizAttr)); 125 | sb.append(", authority:").append(this.authority); 126 | sb.append(", cacheControl:").append(getMemberStringValue(this.cacheControl)); 127 | sb.append(", contentType:").append(getMemberStringValue(this.contentType)); 128 | sb.append(", contentDisposition:").append(getMemberStringValue(this.contentDisposition)); 129 | sb.append(", contentLanguage:").append(getMemberStringValue(this.contentLanguage)); 130 | sb.append(", contentEncoding:").append(getMemberStringValue(this.contentEncoding)); 131 | for (String key : this.xCosMetaHeaders.keySet()) { 132 | sb.append(", x_cos_meta_key:").append(getMemberStringValue(key)); 133 | sb.append(", x_cos_meta_value:") 134 | .append(getMemberStringValue(this.xCosMetaHeaders.get(key))); 135 | } 136 | sb.append(", authority:"); 137 | if (this.authority == null) { 138 | sb.append("null"); 139 | } else { 140 | sb.append(this.authority); 141 | } 142 | return sb.toString(); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/UpdateFolderRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 更新目录请求 8 | * 9 | */ 10 | public class UpdateFolderRequest extends AbstractBaseRequest { 11 | 12 | // biz_attr属性 13 | private String bizAttr = ""; 14 | 15 | public UpdateFolderRequest(String bucketName, String cosPath) { 16 | super(bucketName, cosPath); 17 | } 18 | 19 | public String getBizAttr() { 20 | return bizAttr; 21 | } 22 | 23 | public void setBizAttr(String bizAttr) { 24 | this.bizAttr = bizAttr; 25 | } 26 | 27 | @Override 28 | public void check_param() throws ParamException { 29 | super.check_param(); 30 | CommonParamCheckUtils.AssertLegalCosFolderPath(this.getCosPath()); 31 | CommonParamCheckUtils.AssertNotRootCosPath(this.getCosPath()); 32 | CommonParamCheckUtils.AssertNotNull("biz_attr", this.bizAttr); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | StringBuilder sb = new StringBuilder(); 38 | sb.append(super.toString()); 39 | sb.append(", biz_attr:").append(getMemberStringValue(bizAttr)); 40 | return sb.toString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/UploadFileRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | import com.qcloud.cos.meta.InsertOnly; 6 | 7 | /** 8 | * @author chengwu 上传文件请求,针对文件整体上传,不分片的操作 9 | */ 10 | public class UploadFileRequest extends AbstractBaseRequest { 11 | // 默认最大并发度,这里是16个线程并发发送 12 | private static final int DEFAULT_TASK_NUM = 16; 13 | // 需要上传的路径 14 | private String localPath; 15 | // 上传文件的属性信息 16 | private String bizAttr; 17 | 18 | private byte[] contentBufer = null; 19 | private boolean uploadFromBuffer = false; 20 | 21 | private InsertOnly insertOnly = InsertOnly.NO_OVER_WRITE; 22 | 23 | // 开启sha摘要 24 | protected boolean enableShaDigest = false; 25 | 26 | // 并行任务数 27 | protected int taskNum = DEFAULT_TASK_NUM; 28 | 29 | public UploadFileRequest(String bucketName, String cosPath, String localPath, String bizAttr) { 30 | super(bucketName, cosPath); 31 | this.localPath = localPath; 32 | this.bizAttr = bizAttr; 33 | this.contentBufer = null; 34 | this.uploadFromBuffer = false; 35 | } 36 | 37 | public UploadFileRequest(String bucketName, String cosPath, String localPath) { 38 | this(bucketName, cosPath, localPath, ""); 39 | } 40 | 41 | public UploadFileRequest(String bucketName, String cosPath, byte[] contentBuffer) { 42 | super(bucketName, cosPath); 43 | this.contentBufer = contentBuffer; 44 | this.uploadFromBuffer = true; 45 | this.bizAttr = ""; 46 | } 47 | 48 | public String getBizAttr() { 49 | return bizAttr; 50 | } 51 | 52 | public void setBizAttr(String bizAttr) { 53 | this.bizAttr = bizAttr; 54 | } 55 | 56 | public String getLocalPath() { 57 | return localPath; 58 | } 59 | 60 | public void setLocalPath(String localPath) { 61 | this.localPath = localPath; 62 | this.uploadFromBuffer = false; 63 | } 64 | 65 | public InsertOnly getInsertOnly() { 66 | return insertOnly; 67 | } 68 | 69 | public void setInsertOnly(InsertOnly insertOnly) { 70 | this.insertOnly = insertOnly; 71 | } 72 | 73 | public byte[] getContentBufer() { 74 | return contentBufer; 75 | } 76 | 77 | public void setContentBufer(byte[] contentBufer) { 78 | this.contentBufer = contentBufer; 79 | this.uploadFromBuffer = true; 80 | } 81 | 82 | public boolean isUploadFromBuffer() { 83 | return uploadFromBuffer; 84 | } 85 | 86 | 87 | @Override 88 | public void check_param() throws ParamException { 89 | super.check_param(); 90 | CommonParamCheckUtils.AssertLegalCosFilePath(this.getCosPath()); 91 | CommonParamCheckUtils.AssertNotNull("biz_attr", this.bizAttr); 92 | CommonParamCheckUtils.AssertNotNull("insertOnly", this.insertOnly); 93 | if (!this.uploadFromBuffer) { 94 | CommonParamCheckUtils.AssertLegalLocalFilePath(this.localPath); 95 | } else { 96 | CommonParamCheckUtils.AssertNotNull("contentBufer", contentBufer); 97 | } 98 | } 99 | 100 | public int getTaskNum() { 101 | return taskNum; 102 | } 103 | 104 | public void setTaskNum(int taskNum) { 105 | this.taskNum = taskNum; 106 | } 107 | 108 | public boolean isEnableShaDigest() { 109 | return enableShaDigest; 110 | } 111 | 112 | public void setEnableShaDigest(boolean enableShaDigest) { 113 | this.enableShaDigest = enableShaDigest; 114 | } 115 | 116 | @Override 117 | public String toString() { 118 | StringBuilder sb = new StringBuilder(); 119 | sb.append(super.toString()); 120 | sb.append(", local_path:").append(getMemberStringValue(this.localPath)); 121 | sb.append(", bizAttr:").append(getMemberStringValue(this.bizAttr)); 122 | sb.append(", uploadFromBuffer:").append(this.uploadFromBuffer); 123 | sb.append(", insertonly:"); 124 | if (this.insertOnly == null) { 125 | sb.append("null"); 126 | } else { 127 | sb.append(this.insertOnly.ordinal()); 128 | } 129 | return sb.toString(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/request/UploadSliceFileRequest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.request; 2 | 3 | import com.qcloud.cos.common_utils.CommonParamCheckUtils; 4 | import com.qcloud.cos.exception.ParamException; 5 | 6 | /** 7 | * @author chengwu 文件分片上传请求 8 | */ 9 | public class UploadSliceFileRequest extends UploadFileRequest { 10 | // 默认分片大小1MB 11 | private static final int DEFAULT_SLICE_SIZE = 1024 * 1024; 12 | // 分片大小,单位字节 13 | private int sliceSize = DEFAULT_SLICE_SIZE; 14 | 15 | public UploadSliceFileRequest(UploadFileRequest request) { 16 | super(request.getBucketName(), request.getCosPath(), request.getLocalPath(), 17 | request.getBizAttr()); 18 | } 19 | 20 | public UploadSliceFileRequest(String bucketName, String cosPath, String localPath, 21 | int sliceSize) { 22 | super(bucketName, cosPath, localPath); 23 | this.sliceSize = sliceSize; 24 | } 25 | 26 | public UploadSliceFileRequest(String bucketName, String cosPath, byte[] contentBuffer) { 27 | super(bucketName, cosPath, contentBuffer); 28 | } 29 | 30 | public int getSliceSize() { 31 | return sliceSize; 32 | } 33 | 34 | public void setSliceSize(int sliceSize) { 35 | this.sliceSize = sliceSize; 36 | } 37 | 38 | @Override 39 | public void check_param() throws ParamException { 40 | super.check_param(); 41 | CommonParamCheckUtils.AssertLegalSliceSize(this.sliceSize); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | StringBuilder sb = new StringBuilder(); 47 | sb.append(super.toString()); 48 | sb.append(", sliceSize:").append(this.sliceSize); 49 | sb.append(", taskNum:").append(String.valueOf(this.taskNum)); 50 | sb.append(", enableShaDigest:"); 51 | if (enableShaDigest) { 52 | sb.append("1"); 53 | } else { 54 | sb.append("0"); 55 | } 56 | return sb.toString(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/sign/Credentials.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.sign; 2 | 3 | /** 4 | * @author chengwu 5 | * 鉴权信息, 包括appId, 密钥对 6 | */ 7 | public class Credentials { 8 | private final long appId; 9 | private final String secretId; 10 | private final String secretKey; 11 | 12 | public Credentials(long appId, String secretId, String secretKey) { 13 | super(); 14 | this.appId = appId; 15 | this.secretId = secretId; 16 | this.secretKey = secretKey; 17 | } 18 | 19 | public long getAppId() { 20 | return appId; 21 | } 22 | 23 | public String getSecretId() { 24 | return secretId; 25 | } 26 | 27 | public String getSecretKey() { 28 | return secretKey; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/qcloud/cos/sign/Sign.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.sign; 2 | 3 | import java.util.Random; 4 | 5 | import com.qcloud.cos.common_utils.CommonCodecUtils; 6 | import com.qcloud.cos.common_utils.CommonPathUtils; 7 | import com.qcloud.cos.exception.AbstractCosException; 8 | import com.qcloud.cos.exception.UnknownException; 9 | 10 | /** 11 | * @author chengwu 封装签名类,包括单次,多次以及下载签名 12 | */ 13 | public class Sign { 14 | private static final Random randomGenerator = new Random(); 15 | 16 | /** 17 | * 返回用户访问资源的签名 18 | * 19 | * @param cred 20 | * 包含用户秘钥信息 21 | * @param bucketName 22 | * bucket名 23 | * @param cosPath 24 | * 要签名的cos路径 25 | * @param expired 26 | * 超时时间 27 | * @param uploadFlag 28 | * 除了生成下载签名,其他情况updateFlag皆为true 29 | * @return 返回base64编码的字符串 30 | * @throws AbstractCosException 31 | */ 32 | private static String appSignatureBase(Credentials cred, String bucketName, String cosPath, long expired, 33 | boolean uploadFlag) throws AbstractCosException { 34 | long appId = cred.getAppId(); 35 | String secretId = cred.getSecretId(); 36 | String secretKey = cred.getSecretKey(); 37 | long now = System.currentTimeMillis() / 1000; 38 | int rdm = Math.abs(randomGenerator.nextInt()); 39 | String fileId = null; 40 | if (uploadFlag) { 41 | fileId = String.format("/%d/%s%s", appId, bucketName, cosPath); 42 | } else { 43 | fileId = cosPath; 44 | } 45 | fileId = CommonPathUtils.encodeRemotePath(fileId); 46 | String plainText = String.format("a=%s&k=%s&e=%d&t=%d&r=%d&f=%s&b=%s", appId, secretId, expired, now, rdm, 47 | fileId, bucketName); 48 | 49 | byte[] hmacDigest; 50 | try { 51 | hmacDigest = CommonCodecUtils.HmacSha1(plainText, secretKey); 52 | } catch (Exception e) { 53 | throw new UnknownException(e.getMessage()); 54 | } 55 | byte[] signContent = new byte[hmacDigest.length + plainText.getBytes().length]; 56 | System.arraycopy(hmacDigest, 0, signContent, 0, hmacDigest.length); 57 | System.arraycopy(plainText.getBytes(), 0, signContent, hmacDigest.length, plainText.getBytes().length); 58 | 59 | return CommonCodecUtils.Base64Encode(signContent); 60 | } 61 | 62 | /** 63 | * 获取多次签名, 一段时间内有效, 针对上传文件,重命名文件, 创建目录, 获取文件目录属性, 拉取目录列表 64 | * 65 | * @param bucketName 66 | * bucket名称 67 | * @param cosPath 68 | * 要签名的cos路径 69 | * @param cred 70 | * 用户的身份信息, 包括appid, secret_id和secret_key 71 | * @param expired 72 | * 签名过期时间, UNIX时间戳。如想让签名在30秒后过期, 即可将expired设成当前时间加上30秒 73 | * @return base64编码的字符串 74 | * @throws AbstractCosException 75 | */ 76 | public static String getPeriodEffectiveSign(String bucketName, String cosPath, Credentials cred, long expired) 77 | throws AbstractCosException { 78 | return appSignatureBase(cred, bucketName, cosPath, expired, true); 79 | } 80 | 81 | /** 82 | * 获取单次签名, 一次有效,针对删除和更新文件目录 83 | * 84 | * @param bucketName 85 | * bucket名称 86 | * @param cosPath 87 | * 要签名的cos路径 88 | * @param cred 89 | * 用户的身份信息, 包括appid, secret_id和secret_key 90 | * @return base64编码的字符串 91 | * @throws AbstractCosException 92 | */ 93 | public static String getOneEffectiveSign(String bucketName, String cosPath, Credentials cred) 94 | throws AbstractCosException { 95 | return appSignatureBase(cred, bucketName, cosPath, 0, true); 96 | } 97 | 98 | /** 99 | * 下载签名, 用于获取后拼接成下载链接,下载私有bucket的文件 100 | * 101 | * @param bucketName 102 | * bucket名称 103 | * @param cosPath 104 | * 要签名的cos路径 105 | * @param cred 106 | * 用户的身份信息, 包括appid, secret_id和secret_key 107 | * @param expired 108 | * 签名过期时间, UNIX时间戳。如想让签名在30秒后过期, 即可将expired设成当前时间加上30秒 109 | * @return base64编码的字符串 110 | * @throws AbstractCosException 111 | */ 112 | public static String getDownLoadSign(String bucketName, String cosPath, Credentials cred, long expired) 113 | throws AbstractCosException { 114 | return appSignatureBase(cred, bucketName, cosPath, expired, false); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/com/qcloud/cos/common_utils/CommonCodecUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.qcloud.cos.common_utils; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | import com.qcloud.cos.common_utils.CommonCodecUtils; 8 | 9 | public class CommonCodecUtilsTest { 10 | 11 | private static final String plainText = "681805d9f7c6ab988a00c02f1096b1b68a77aaed"; 12 | private static final String hmacKey = "lw7231!2@7g"; 13 | 14 | @Test 15 | public void testBase64Encode() { 16 | try { 17 | String encodeStr = CommonCodecUtils.Base64Encode(plainText.getBytes("UTF-8")); 18 | String expectEncodeStr = "NjgxODA1ZDlmN2M2YWI5ODhhMDBjMDJmMTA5NmIxYjY4YTc3YWFlZA=="; 19 | boolean cmpResult = encodeStr.equals(expectEncodeStr); 20 | assertTrue(cmpResult); 21 | } catch (Exception e) { 22 | fail(); 23 | } 24 | } 25 | 26 | @Test 27 | public void testHmacSha1StringString() { 28 | try { 29 | byte[] hmacDigestByte = CommonCodecUtils.HmacSha1(plainText, hmacKey); 30 | StringBuilder stringBuilder = new StringBuilder(); 31 | for (int i = 0; i < hmacDigestByte.length; ++i) { 32 | String hex = Integer.toHexString(hmacDigestByte[i] & 0xff); 33 | if (hex.length() == 1) { 34 | stringBuilder.append("0"); 35 | } 36 | stringBuilder.append(hex); 37 | } 38 | String expectHmacDigest = "e8d7985289f8586a7bd2374590db848c48046874"; 39 | assertTrue(expectHmacDigest.equals(stringBuilder.toString())); 40 | } catch (Exception e) { 41 | fail(); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/resources/bigfile.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tencentyun/cos-java-sdk-v4/dec54d566b09ed8795de5d6d07d7b36a2076159f/src/test/resources/bigfile.txt -------------------------------------------------------------------------------- /src/test/resources/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tencentyun/cos-java-sdk-v4/dec54d566b09ed8795de5d6d07d7b36a2076159f/src/test/resources/empty.txt -------------------------------------------------------------------------------- /src/test/resources/local_file_1.txt: -------------------------------------------------------------------------------- 1 | 18544556699 -------------------------------------------------------------------------------- /src/test/resources/local_file_2.txt: -------------------------------------------------------------------------------- 1 | 15033556677 -------------------------------------------------------------------------------- /src/test/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | --------------------------------------------------------------------------------