文档中心 > 店铺动态卡片-开发指引

PC 千牛上传图片常见问题

更新时间:2023/07/07 访问次数:5033

问题背景

PC 千牛小程序里,能够用于上传文件的 API 只有 my.uploadFile。关于此 API 的试用,详见 API 文档补充使用说明

因为上传图片是几乎每个小程序都需要使用到的功能,而我们经常接到上传图片相关的咨询,因此针对咨询中的热点问题,整理出本文档。

域名管控

Q:使用 my.uploadFile 需要申请白名单吗?

A:需要。请提交工单申请。

Q:为何阿里系域名还要申请白名单?

A:因为并不是所有阿里系域名的内容都是集团直接管控的。例如阿里云的用户内容,也会使用阿里云的域名。如果您使用 my.uploadFile 时报错为“no permission”,就说明是域名管控导致。

上传相关

Q:我应该选择什么方式上传文件

A:一般推荐使用商家云存储或图片空间。如果有需要上传到自有服务端,请通过小程序云对接。

原则上不允许直接上传到自有服务器,或者上传到公有云服务,除非业务确实有需要、运营同意开白名单。

Q:如何上传图片到商家云存储

A:参考文档:

 

前置业务流程:

  • 开通云服务

 

示例代码:

import { Cloud } from '@tbmp/mp-cloud-sdk'; 
const cloud = new Cloud();
cloud.init({
env: 'test' // 线上版本请使用 online
});
// 本例为了简单起见,先让用户选择一个文件,得到 apFilePath,实际业务中可使用其他 API 产生的 apFilePath
my.chooseImage({
success: (res) => {
const apFilePath = res['apFilePaths'][0]
cloud.file.uploadFile({
filePath: apFilePath,
fileType: 'image',
fileName: 'test_file_name',
})
}
})

Q:如何上传图片到商家图片空间

A:参考文档:

 

前置业务流程:

  • 开通云服务

 

示例代码:

import { Cloud } from '@tbmp/mp-cloud-sdk';
const cloud = new Cloud();
cloud.init({
env: 'test' // 线上版本请使用 online
});
// 本例为了简单起见,先让用户选择一个文件,得到 apFilePath,实际业务中可使用其他 API 产生的 apFilePath
my.chooseImage({
success: (res) => {
const apFilePath = res['apFilePaths'][0]
cloud.file.uploadFile({
filePath: apFilePath,
fileType: 'image',
fileName: 'test_file_name',
seller: true
})
}
})

Q:如何上传图片到阿里云 OSS

A:参考文档:

 

示例代码:

const btoa = require('btoa'); // 计算 base64 用 
const hmacsha1 = require('hmacsha1') // 计算 hmac-sha1 用
// 本例为了简单起见,先让用户选择一个文件,得到 apFilePath,实际业务中可使用其他 API 产生的 apFilePath
my.chooseImage({
success: (res) => {
const apFilePath = res['apFilePaths'][0]
const accessKeyId = '<YOUR ACCESS KEY ID>'
const accessKeySecret = '<YOUR ACCESS KEY SECRET>'
const url = 'https://<BUCKET>.oss-cn-<REGION>.aliyuncs.com/'
const policy = {
expiration: new Date(new Date().getTime() + 60 * 1000).toISOString(),
conditions: [
["eq", "$key", "oss_file_name"] // 至少需要有一个条件,此处仅示意,请根据实际情况修改

]

}
const encodedPolicy = btoa(JSON.stringify(policy))
// 计算签名,文档中描述的算法为:Signature = base64(hmac-sha1(base64(policy), AccessKeySecret))

// 但因为我们这里用的 hmacsha1 库里面会做 base64,所以要省去最外层的 base64

const signature = hmacsha1(accessKeySecret, encodedPolicy)
my.uploadFile({
url: url,
filePath: apFilePath,
fileName: 'source_file_name',
formData: {
key: 'oss_file_name',
OSSAccessKeyId: accessKeyId,
policy: encodedPolicy,
Signature: signature,
},
complete: (res) => {
console.log(res)
}
})
}
})

Q:如何上传图片到自有服务端

A:使用 my.uploadFile,服务端接收 multipart/form-data 表单并处理文件即可。

 

服务端示例代码:

(简单起见,下列代码都是将被上传的文件保存到 /tmp,请根据情况自行调整)

Java Spring Boot:

@RequestMapping("/upload") 
public ResponseEntity upload(MultipartFile[] files) throws IOException {
for (MultipartFile f : files) {
f.transferTo(new File("/tmp/" + f.getOriginalFilename()));
}
return new ResponseEntity(HttpStatus.OK);
}

Java Servlet:

@WebServlet(name = "Upload")
@MultipartConfig
public class Upload extends HttpServlet {
// For Servlet 3.0
private static String getSubmittedFileName(Part part) {
for (String cd : part.getHeader("content-disposition").split("; ")) {
if (cd.startsWith("filename=")) {
// filename="SUBMITTED_FILE_NAME"

return cd.substring(10, cd.length() - 1);
}
}

return null;

}


@Override

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Collection<Part> parts = request.getParts();
for (Part p : parts) {
String filename = getSubmittedFileName(p);
// Using p.getSubmittedFileName() for Servlet 3.1 or above
p.write("/tmp/" + filename); p.delete();
}
response.setStatus(HttpServletResponse.SC_OK);
}
}

Go:

func UploadHandler(w http.ResponseWriter, req *http.Request) {
req.ParseMultipartForm(1 * 1024 * 1024) // 简单起见,不处理错误,下同
for _, files := range req.MultipartForm.File {
for _, f := range files {
src, _ := f.Open()
dst, _ := os.Create("/tmp/" + f.Filename)
io.Copy(dst, src) dst.Close()
}
}
w.WriteHeader(http.StatusOK)
}

PHP:

<?php
foreach ($_FILES as $f) {
move_uploaded_file($f["tmp_name"], '/tmp/' . basename($f["name"]));
}

NodeJS:

const formidable = require('formidable');
const http = require('http');
const mv = require('mv');
http.createServer(function (request, response) {
if (request.url == '/upload' && request.method.toLowerCase() == 'post') {
const form = new formidable.IncomingForm()
form.parse(request, function (err, fields, files) {
for (let i in files) {
mv(files[i].path, '/tmp/' + files[i].name, {}, (err) => { })
}
response.writeHead(200)
response.end()
});
return;
}
}).listen(80);

Python:

class UploadServer(BaseHTTPRequestHandler):
def do_POST(self):
form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={
"REQUEST_METHOD": "POST",
})

for i in form:

with open('/tmp/' + form[i].filename, "wb") as f:
f.write(form[i].file.read())
self.send_response(200)
self.end_headers()

 

小程序端示例代码:

// 本例为了简单起见,先让用户选择一个文件,得到 apFilePath,实际业务中可使用其他 API 产生的 apFilePath 
my.chooseImage({
success: (res) => {
const apFilePath = res['apFilePaths'][0]
my.uploadFile({
url: '<SERVER_URL>',
fileType: 'image',
fileName: 'client_file_name',
filePath: apFilePath,
complete: (res) => {
console.log(res);
},
});
},
})

Q:如何通过云函数、云应用上传图片到自有服务器

A:由于小程序这边的 API 中,支持使用 multipart/form-data 协议直接上传的只有 my.uploadFile 这一个 API。而云应用、云函数通过 MTOP 网关中转,网关并不支持 multipart/form-data 协议,因此需要读取文件内容,然后当作一个普通字段传输。此外,MTOP 有 1MB 的报文大小限制,因此这种方式传输文件,文件内容也不能超过 1MB。

 

参考文档:

 

示例代码:

// 本例为了简单起见,先让用户选择一个文件,得到 apFilePath,实际业务中可使用其他 API 产生的 apFilePath 
my.qn.chooseFileAndGetContent({
success: (res) => {
const fileContentMap = res['fileContentMap']
for (let name in fileContentMap) {
// 将 fileContentMap[name] 传给 cloud.function.invoke 或 cloud.application.httpRequest 等
}

},
})

 

FAQ

关于此文档暂时还没有FAQ
返回
顶部