对象存储
- 阿里云OSS对象存储(后端技术编程语言)
- 准备工作
- 进入阿里云官网,注册账号,登录(需要实名认证),最好是使用支付宝账号登录
- 开通对象存储OSS,一般情况下是有一个免费(3个月)的,当然也可以购买开通(就几毛钱/月)
- 创建一个专门为项目服务的Bucket
- 代码上传文件到对象存储OSS
- 找到Bucket的Access Key的按钮,创建新的Access Key以及找到endpoint(外网的)、bucketname
- AccessKey ID:tghhsbgrergesgregaerr
- AccessKey Secret:rehgrehgfdgafhrtjhtyrjukt
- endpoint
- bucketname
- 导入aliyun-sdk-oss依赖
<!--aliyunOSS--> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.10.2</version> </dependency> <!--日期时间工具--> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.10.1</version> </dependency>
- 配置文件添加内容(可在当前工程中配置文件添加)
# 服务端口 server.port=8002 # 服务名 spring.application.name=service-oss # 环境设置:dev、test、prod spring.profiles.active=dev # 阿里云OSS # 不同的服务器,地址不同 aliyun.oss.file.endpoint=your endpoint aliyun.oss.file.keyid=your accessKeyId aliyun.oss.file.keysecret=your accessKeySecret # bucket可以在控制台(阿里云中)创建,也可以使用代码创建 aliyun.oss.file.bucketname=your bucketName
- 快速入门(代码片段)
- 注意事项:如果文件类型是图片,则使用MultipartFile类的对象,如MultipartFile file(比较推荐)
- 适用于流式文件传输,在上传图片,音/视频,文档等,都可采用
- 获取输入流的方式
file.getInputStream()
- 获取文件原始名称的方式
file.getOriginalFilename()
- 创建存储空间
public class Demo { public static void main(String[] args) throws Exception { // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。关于其他Region对应的Endpoint信息,请参见访问域名和数据中心。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // 填写Bucket名称,例如examplebucket。 String bucketName = "examplebucket"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); try { // 创建存储空间。 ossClient.createBucket(bucketName); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
- 上传文件
import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import java.io.ByteArrayInputStream; public class Demo { public static void main(String[] args) throws Exception { // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // 填写Bucket名称,例如examplebucket。 String bucketName = "examplebucket"; // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。 String objectName = "exampledir/exampleobject.txt"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); try { String content = "Hello OSS"; ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content.getBytes())); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
- 下载文件
import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import com.aliyun.oss.model.OSSObject; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; public class Demo { public static void main(String[] args) throws Exception { // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // 填写Bucket名称,例如examplebucket。 String bucketName = "examplebucket"; // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。 String objectName = "exampledir/exampleobject.txt"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); try { // 调用ossClient.getObject返回一个OSSObject实例,该实例包含文件内容及文件元信息。 OSSObject ossObject = ossClient.getObject(bucketName, objectName); // 调用ossObject.getObjectContent获取文件输入流,可读取此输入流获取其内容。 InputStream content = ossObject.getObjectContent(); if (content != null) { BufferedReader reader = new BufferedReader(new InputStreamReader(content)); while (true) { String line = reader.readLine(); if (line == null) break; System.out.println("\n" + line); } // 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。 content.close(); } } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
- 列举文件
import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import com.aliyun.oss.model.OSSObjectSummary; import com.aliyun.oss.model.ObjectListing; public class Demo { public static void main(String[] args) throws Exception { // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // 填写Bucket名称,例如examplebucket。 String bucketName = "examplebucket"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); try { // ossClient.listObjects返回ObjectListing实例,包含此次listObject请求的返回结果。 ObjectListing objectListing = ossClient.listObjects(bucketName); // objectListing.getObjectSummaries获取所有文件的描述信息。 for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) { System.out.println(" - " + objectSummary.getKey() + " " + "(size = " + objectSummary.getSize() + ")"); } } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
- 删除文件
import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; public class Demo { public static void main(String[] args) throws Exception { // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // 填写Bucket名称,例如examplebucket。 String bucketName = "examplebucket"; // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。 String objectName = "exampledir/exampleobject.txt"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); try { // 删除文件。 ossClient.deleteObject(bucketName, objectName); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
- 注意事项:如果文件类型是图片,则使用MultipartFile类的对象,如MultipartFile file(比较推荐)
- 当前工程中的启动类启动时会报错
- 原因是没有在当前工程中配置数据库的配置
- 解决方式
- 添加上数据库的配置
- 在启动类中添加不去加载数据库配置的属性(不涉及数据库时,尽量使用这种方式)
- 示例如下
package com.xsha.oss; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @ComponentScan(basePackages = {"com.xsha"}) public class OssApplication { public static void main(String[] args) { SpringApplication.run(OssApplication.class, args); } }
- 当类中要使用到配置文件的常量时,我们尽量创建一个
常量类
去读取配置文件的常量,就如上面的AccessKey相关信息package com.xsha.oss.utils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; // 2.当项目启动时,Spring加载接口的一个方法 @Component public class ConstantPropertiesUtils implements InitializingBean { // 1.读取配置文件内容 @Value("${aliyun.oss.file.endpoint}") private String endpoint; @Value("${aliyun.oss.file.keyid}") private String keyId; @Value("${aliyun.oss.file.keysecret}") private String keySecret; @Value("${aliyun.oss.file.bucketname}") private String bucketName; // 3.定义公开静态常量 public static String END_POINT; public static String KEY_ID; public static String KEY_SECRET; public static String BUCKET_NAME; // 4.进行赋值 @Override public void afterPropertiesSet() throws Exception { END_POINT = endpoint; KEY_ID = keyId; KEY_SECRET = keySecret; BUCKET_NAME = bucketName; } }
- 在controller层或者service层(最好写在这边)的类中具体实现文件管理的需求,如上传文件
package com.xsha.oss.service.impl; import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import com.xsha.oss.service.OssService; import com.xsha.oss.utils.ConstantPropertiesUtils; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; @Service public class OssServiceImpl implements OssService { @Override public String uploadFileAvatar(MultipartFile file) { // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。 String endpoint = ConstantPropertiesUtils.END_POINT; // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 String accessKeyId = ConstantPropertiesUtils.KEY_ID; String accessKeySecret = ConstantPropertiesUtils.KEY_SECRET; // 填写Bucket名称,例如examplebucket。 String bucketName = ConstantPropertiesUtils.BUCKET_NAME; // 获取文件的原始名称(最好是生成随机性的名称,防止重名覆盖原文件) String filename = file.getOriginalFilename(); String uuid = UUID.randomUUID().toString().replaceAll("-", ""); filename = uuid+filename; // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。 // 可以将文件存放在以时间命名的目录中 SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd/"); Date date = new Date(); String datePath = df.format(date); String objectName = datePath+filename; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); try { // 文件的输入流 InputStream inputStream = file.getInputStream(); // 创建PutObject请求。 ossClient.putObject(bucketName, objectName, inputStream); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); return null; } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); return null; } catch (IOException e) { e.printStackTrace(); return null; } finally { if (ossClient != null) { ossClient.shutdown(); } // 返回上传文件成功的路径 // 需要把路径拼接 String url = "https://"+bucketName+"."+endpoint+"/"+filename; return url; } } }
- 找到Bucket的Access Key的按钮,创建新的Access Key以及找到endpoint(外网的)、bucketname
- 准备工作
- 腾讯云COS对象存储(小程序)
- 准备工作
- 进入腾讯云官网,注册账号,登录,最好是使用微信扫码登录
- 开通对象存储COS,一般情况下是有一个免费(3个月)的,当然也可以购买开通(就几毛钱/月)
- 创建一个专门为项目服务的Bucket
- 代码上传文件到对象存储OSS
- 找到Bucket的Access Key的按钮,创建新的Access Key以及找到region、bucket
- AccessKey ID:tghhsbgrergesgregaerr
- AccessKey Secret:rehgrehgfdgafhrtjhtyrjukt
- region
- bucket
- 安装python的sdk(python作为后端技术编程语言)
pip install -U cos-python-sdk-v5
- 在配置文件中添加内容
# 腾讯云密钥 TENCENT_SECRET_ID = "tghhsbgrergesgregaerr" TENCENT_SECRET_KEY = "rehgrehgfdgafhrtjhtyrjukt" # 腾讯云bucket名称 TENCENT_BUCKET = "your bucket" # 腾讯云COS区域 TENCENT_REGION = "the region of your bucket"
- python需要编写一个返回“临时密钥”的接口,将信息返回给前端,前端计算签名
class CreateTempSecretView(APIView): @staticmethod def get(request): config = { # 临时密钥有效时长,单位是秒 'duration_seconds': 1800, 'secret_id': TENCENT_SECRET_ID, # 固定密钥 'secret_key': TENCENT_SECRET_KEY, # 换成你的 bucket 'bucket': TENCENT_BUCKET, # 换成 bucket 所在地区 'region': TENCENT_REGION, # 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径 # 例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用) 'allow_prefix': '*', # 密钥的权限列表。简单上传和分片需要以下的权限, # 其他权限列表请看 https://cloud.tencent.com/document/product/436/31923 'allow_actions': [ # 简单上传 'name/cos:PutObject', 'name/cos:PostObject', # 删除对象 "name/cos:DeleteObject" ], } try: sts = Sts(config) response = sts.get_credential() print('get data : ' + json.dumps(dict(response), indent=4)) return Response(data=response, status=status.HTTP_200_OK) except Exception as e: print(e) return Response(data={"error": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
- 前端(小程序)获取COS对象存储对象
onLoad: function (options) { userDir = app.globalData.userInfo.nickName; // 获取上传对象 var cos = new COS({ // 必选参数 getAuthorization: function (options, callback) { wx.request({ url: api.createTempSecretUrl, data: { // 可从 options 取需要的参数 }, header: { Authorization: app.globalData.userInfo? app.globalData.userInfo.token : null, }, success: function (result) { var data = result.data; var credentials = data && data.credentials; if (!data || !credentials) return console.error('credentials invalid'); callback({ TmpSecretId: credentials.tmpSecretId, TmpSecretKey: credentials.tmpSecretKey, XCosSecurityToken: credentials.sessionToken, // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 StartTime: data.startTime, // 时间戳,单位秒,如:1580000000 ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900 }); } }); } }); this.setData({ cos: cos, }) }
- 获取对象后就可以管理文件
- 上传文件
/** * 获取图片 */ getPicture: function() { var that = this; wx.chooseImage({ count: 9, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success: function(res) { // 在当前页面显示上传的图片 // 将各个图片制作成一个个“字典”类型 var oldLen = that.data.pictureInfo.length; for(let index in res.tempFilePaths) { var temp = {}; temp["id"]=parseInt(oldLen)+parseInt(index); temp["path"]=res.tempFilePaths[index]; temp["percent"]=0; // 控制当前图片显示的状态true为显示,false隐藏 temp["active"] = true; // 将自定义的图片地址等其他信息放置在内存中 that.data.pictureInfo.push(temp); } // 我也不知道为什么,就当是数据刷新了(下段代码就是显示当前选择的图片) that.setData({ pictureInfo: that.data.pictureInfo, }); // 通过该页面定义的上传对象逐一将新照片上传到腾讯云对象存储中 for(let index in res.tempFilePaths) { // 采用闭包的形式上传对象 (function(i){ // 文件路径和文件名定义好 var filePath = res.tempFilePaths[i]; var filename = filePath.substr(filePath.lastIndexOf('/') + 1); // 上传到对象存储中 that.data.cos.postObject({ Bucket: 'your bucket name', Region: 'your region', Key: '上传之后的目录/'+userDir+"/"+ filename, FilePath: filePath, onProgress: function (info) { // 重新赋值进度条的进度 that.setData({ ["pictureInfo["+parseInt(oldLen)+parseInt(i)+"].percent"]: info.percent*100, }); } }, function (err, data) { // 将存储到对象存储的真实照片网络地址弄到内存中,其实应该存放到后端数据库中 that.data.cos_path.push(data.Location); }); })(index); } } }); }
- 删除文件
/** * 取消上传的图片(删除存储对象中的图片) */ deletePicture:function(res){ // 获取删除对象(图片)的地址 var that = this; var path = res.currentTarget.dataset.path; var filename = path.substr(path.lastIndexOf('/') + 1); // 获取当前内存中图片的索引 var ind = res.currentTarget.dataset.id; // 之后可以将值传给后端存储起来,这里暂时省略 // 这里先存储到腾讯云对象存储中 that.data.cos.deleteObject({ Bucket: 'your bucket name', Region: 'your region', Key: '上传之后的目录'+userDir+"/"+ filename, }, function (err, data) { // 隐藏并移除页面中暂放的选中删除的图片 that.setData({ ['pictureInfo['+ind+'].active']: false, }) that.data.pictureInfo.splice(ind, 1); // 获取完整的地址 var picture = "your bucket name/上传之后的目录/"+userDir+"/"+filename; // 获取当前图片在内存中的地址 var index = that.data.cos_path.indexOf(picture); // 移除当前取消上传的图片,以防给到后端存储 that.data.cos_path.splice(index, 1); }); },
- 上传文件
- 找到Bucket的Access Key的按钮,创建新的Access Key以及找到region、bucket
- 准备工作
- 注意:千万不要局限于编程语言,高级编程语言都是相通的,这两种对象存储步骤基本上一致的。