这是小程序开发第二篇,主要介绍如何上传图片到腾讯云。之所以选择腾讯云,是因为腾讯云免费空间大。准备工作包括购买腾讯云对象存储(COS)服务、在腾讯云对象存储控制台里创建一个Bucket、在控制台个人API密钥页面里获取AppID、SecretID、SecretKey内容、编写一个请求签名算法程序或使用任何一种服务端SDK、计算签名并调用API执行操作。

要将图片上传到腾讯云对象存储(COS),需要先进入腾讯云官网注册帐号,登录云对象存储服务(COS)控制台开通COS服务并创建资源需要上传的Bucket。在小程序官网上配置域名信息(否则无法在小程序中发起对该域名的请求)。接下来主要介绍步骤4、5,即小程序上传图片到cos的流程。在这个过程中需要实现的是鉴权服务器返回签名的步骤以及小程序的相关步骤。

COS鉴权服务可以通过RESTful API对COS发起HTTP匿名请求或HTTP签名请求。对于签名请求,COS服务器端将会进行对请求发起者的身份验证。匿名请求:HTTP请求不携带任何身份标识和鉴权信息,通过RESTfulAPI进行HTTP请求操作。签名请求:HTTP请求时添加签名,COS服务器端收到消息后,进行身份验证,验证成功则可接受并执行请求,否则将会返回错误信息并丢弃此请求。

腾讯云COS对象存储基于密钥HMAC (Hash Message Authentication Code) 的自定义HTTP方案进行身份验证。上传图片是一个签名请求,需要进行签名验证。客户通过对HTTP请求进行签名,并将签名后的请求发送至腾讯云进行签名验证。具体流程如下图所示。我们使用sdk开发,这个流程大致了解下就行,签名的实现sdk已经包含,只需要调用方法即可。

通过签名流程我们可以知道,签名需要SecretId和SecretKey这两个信息不适合存放在客户端中,这也是我们多带带部署一个鉴权服务器的主要原因。最后介绍了签名生成API。

上一篇[小程序开发:python sanic 实现小程序登录注册]()我们介绍过,服务端使用sanic框架+swagger_py_codegen生成rest-api。添加签名生成API我们需要先在文档中添加API的相关描述。文档代码:https://github.com/gusibi/Metis/blob/master/docs/v1.yml

```yaml

/qc_cos/config: get: summary: 腾讯云配置 description: 腾讯云配置 tags: [Config] operationId: get_qc_cos_config parameters: - $ref: "#/parameters/AccessToken" - $ref: "#/parameters/qcos_path_in_query" responses: 200: schema: $ref: "#/definitions/QCOSConfig" default: description: Unexpected error schema: $ref: "#/definitions/Error" security: - OAuth2: [open]

```

这个接口我们要求登录才能调用。文档定义完成之后,调用swagger_py_codegen -s docs/v1.yml . -p apis -tlp sanic生成代码模板,API代码实现如下:

以下是根据您提供的内容重构后的代码:

```python

from qcloud_cos.cos_auth import Auth

import time

from config import Config

from sanic import Sanic, response

import wx

app = Sanic(__name__)

@app.route("/upload", methods=["GET"])

async def upload(request):

auth = Auth(appid=Config.QCOS_APPID,

secret_id=Config.QCOS_SECRET_ID,

secret_key=Config.QCOS_SECRET_KEY)

expired = time.time() + 3600 # 签名有效时间 3600 秒

dir_name = request.args.get("cos_path", "/xrzeti")

sign = auth.sign_more(Config.QCOS_BUCKET_NAME,

cos_path=dir_name,

expired=expired)

return {"sign": sign}, 200

def choose_image():

obj = wx.ObjectData()

obj.SetPath(wx.FileName("temp.jpg"))

res = wx.chooseImage(obj)

if res:

return res[0]

else:

return None

if __name__ == "__main__":

app.run(host="0.0.0.0", port=8000)

```

这段代码是一个简单的 Sanic Web 服务器,用于处理上传图片到腾讯云 COSv4 的请求。首先,我们导入了所需的库和模块,然后定义了一个名为 `upload` 的异步函数,该函数接收一个 GET 请求,并返回一个包含签名的 JSON 对象。接下来,我们定义了一个名为 `choose_image` 的函数,用于从微信小程序中选择图片。最后,我们运行了 Sanic Web 服务器。

uploadToCos: function () { var that = this;

// 选择上传的图片

wx.chooseImage({

sizeType: ['original', 'compressed'], // 图片类型:原图、压缩图,默认二者都有

success: function (res) {

// 获取文件路径

var file = res.tempFiles[0];

console.log(file.size);

// 获取文件名

var fileName = file.path.match(/(wxfile://)(.+)/)[2];

fileName = fileName;

// 获取到图片临时路径后,指定文件名上传到cos

var cosUrl = "https://" + REGION + ".file.myqcloud.com/files/v2/" + APPID + "/" + BUCKET_NAME + "/" + DIR_NAME;

upload(file.path, fileName, that, cosUrl);

}

});

```javascript// 配置文件

var config = require("../config.js"); // 确定上传的URL

var cosUrl = "https://" + config.cos_region + ".file.myqcloud.com/files/v2/" + config.cos_appid + "/" + config.cos_bucket_name + config.cos_dir_name; // 填写自己的鉴权服务器地址

var cosSignatureUrl = config.host + "/v1/qc_cos/config?cos_path=" + config.cos_dir_name;

// 上传方法

/**

* 文件路径: 需上传的文件路径

* 文件名: 上传到cos后的文件名

* that: 小程序所在当前页面的对象

*/

function upload(filePath, fileName, that) {

var data;

// 通过鉴权获取签名

wx.request({

url: cosSignatureUrl,

header: {

Authorization: "JWT" + " " + that.data.jwt.access_token

},

success: function (cosRes) {

// 获取签名

var signature = cosRes.data.sign;

// 头部带上签名,上传文件至COS

var uploadTask = wx.uploadFile({

url: cosUrl + "/" + fileName,

filePath: filePath,

header: {

Authorization: signature

},

name: "filecontent",

formData: {

op: "upload"

},

success: function (uploadRes) {

// 上传成功后的操作

var upload_res = JSON.parse(uploadRes.data);

var files = that.data.files;

files.push(upload_res.data.source_url);

that.setData({

upload_res: upload_res,

files: files,

test_image: upload_res.data.source_url

});

},

fail: function (e) {

console.log("e", e);

}

});

// 上传进度条显示更新函数

uploadTask.onProgressUpdate((res) => {

that.setData({

upload_progress: res.progress

});

if (res.progress === 100) {

that.setData({

upload_progress: 0

});

}

});

}

});

}

```

以下是重构后的代码:

```java

private String getPath(Uri uri) {

String result;

if (uri.getScheme().equals("file")) {

try {

result = uri.getPath();

} catch (Exception e) {

result = null;

}

} else if (uri.getScheme().equals("content")) {

result = uri.getAuthority();

} else {

result = "";

}

return result;

}

private void uploadImageToCOS() {

/****上传图片到COS****/

int maxLength = Integer.MAX_VALUE;//最大上传文件大小(字节)

int maxLengthLong = Long.MAX_VALUE;//最大上传文件大小(字节)

int maxNum = Integer.MAX_VALUE;//最大同时上传文件数量

int progressInterval = 100;//更新进度间隔时间(单位为秒)

HashMap headerMap = new HashMap<>();//设置请求头信息

headerMap.put("Authorization", "your access key");//accessKeySecret是secretId的临时密钥,在获取cos服务时可以得到。

headerMap.put("Content-Type", "multipart/form-data");

headerMap.put("User-Agent", "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36");

//headerMap.put("Host", "your bucket name");//host是cos服务的域名,如果不填会默认使用bucket的子域名作为域名。

headerMap.put("Expect", "100-continue");//开启二进制流传输时需要设置此项,否则可能造成请求中断。

//headerMap.put("x-amz-acl", "public-read");//设置该字段为“public-read”,可对所有账户开放读权限。

//headerMap.put("x-amz-server-side-encryption","AES256");//服务器端加密方式,可选aes256、aes128、kms三种加密方式。

//headerMap.put("x-amz-storage-class","STANDARD");//可选标准、低频、冷归档三种存储类型。

//headerMap.put("x-amz-website-redirect","false");//是否允许重定向,可选true或false。

//headerMap.put("x-amz-meta-*","*");//设置metadata信息的HTTP头字段。如果该字段被包含在Content-MD5中,将忽略此设置,并返回错误。

//headerMap.put("Cache-Control", "no-cache");//缓存控制指令,可选max-age=XXX、no-transform、no-store、must-revalidate几种方式。

//headerMap.put("Content-Disposition", "attachment;filename=\"test.png\"");//指定文件名和下载时的名称。若该值未指定,则从请求体中的文件数据中提取出文件名。

//headerMap.put("Content-Range", "bytes */"+maxLength);//指定上传文件的范围,例如bytes */1024表示上传整个文件。若该值未指定,则使用默认范围:bytes */。注意:如果使用了分块上传,则必须明确指定范围信息,否则无法进行断点续传。

//headerMap.put("Expires", "0");//过期时间,即HTTP响应头中的Date字段的值,表示资源的过期时间,单位为秒,若不设置则表示永不过期。

//headerMap.put("Content-Encoding", "gzip、deflate、identity");//指定内容编码方式。若未提供该参数,则根据请求头中Accept-Encoding的值来判断。可选压缩编码方式有:gzip、deflate、identity三种。对于文本数据不建议使用压缩编码方式。

//headerMap.put("ETag", "\"etagvalue\"");//资源标记,用于唯一标识一个资源对象。当客户端再次发送相同URL的请求时,如果资源内容没有变化,则服务器会返回304 Not modified状态码,表示客户端可以使用本地缓存的数据继续处理请求。如果资源内容发生变化,则返回新的ETag值供客户端校验。若未提供该值,则默认使用资源的Lastmodified时间计算得出ETag值。

//headerMap.put("Pragma", "\"cache\"");//缓存控制指令,可选no-cache、no-store、no-transform、max-age等几种方式,具体含义见上述Cache-Control指令说明。若未提供该值,则默认不进行缓存控制操作。

//headerMap.put("Range", "bytes=0-1023");//指定上传文件的字节范围,例如bytes=0-1023表示只上传文件的前1024个字节。若未使用分块上传功能,则不需要设置该参数。若使用分块上传功能并且已经设置了Content-Length头信息,则该参数无效。

//headerMap.put("Connection", "keep-alive");//连接保持状态,可选yes或close两种状态。若设置为yes,则表示在完成当前请求后仍然保持连接状态,直到收到新请求为止;若设置为close,则表示在完成当前请求后立即断开连接。默认情况下是close状态。注意:在上传完成后应尽快关闭连接。

//headerMap.put("Access-Control-Allow-Origin","*");//跨域访问配置信息,可选*或者http://domain/*两种模式。若仅使用简单跨域访问配置(即*通配符),则必须将域名显式添加到该配置项中。如果使用了更严格的跨域访问规则(即http://domain/*通配符),则必须将域名显式添加到Access Control Allow Origin头部字段中才能实现跨域访问功能。注意:由于微信小程序的安全性限制,只有当小程序域名与服务器域名一致时才支持跨域访问配置(即Access-Control-Allow Origin头部字段中只能填写小程序域名)。如果需要支持更多域名的跨域访问配置(即http://domain/*通配符),则需要使用JSONP方式实现跨域访问功能。另外还需要在服务器端进行相应的跨域访问配置(如CORS配置),详见下文介绍。

```