Taro小程序开发
系列文章的所有文章的目录
【Taro开发】-简易的checkBoxGroup组件(九)
【Taro开发】-宣传海报,实现canvas实现圆角画布/图片拼接二维码并保存(十一)
【Taro开发】-小程序自动打包上传并生成预览二维码(十三)
【Taro开发】-全局自定义导航栏适配消息通知框位置及其他问题(十四)
前言
基于Taro的微信小程序开发,主要组件库为Taro-ui
由于小程序里没有FormData类,所以POST方法如果要传multipart/form-data就会报错,所以手动拼装FormData格式的字符串。
提示:以下是本篇文章正文内容,下面案例可供参考
一、图片上传
1. 常量定义
//常量定义
const boundary = `----WebKitFormBoundaryX2UFH67x5M0xltNB`
2. 手动拼接FormData字符串
以下是我拼装及需要的格式:
// 手动拼接FormData字符串,可自行补充修改
export function createFormData(params) {
let result = '';
for (let i in params) {
result += `\r\n--${boundary}`;
if (i === 'file') {
result += `\r\nContent-Disposition: form-data; name="${i}"; filename="${params['fileName']}"`;
result += `\r\nContent-Type: ${params[i]?.type}`;
result += '\r\n';
result += '\r\n';
} else {
result += `\r\nContent-Disposition: form-data; name="${i}"`;
result += '\r\n';
result += `\r\n${params[i]}`
}
}
// 如果obj不为空,则最后一行加上boundary
if (result) {
result += `\r\n--${boundary}--`
}
return result
}
3. 接口调用
function upload(params) {
return httpService.post(`...`, {
data: params,
headers: {
contentType: `multipart/form-data;boundary=${boundary}`,
},
});
}
网络请求部分可参考【Taro开发】-带token网络请求封装(四)
二、ImgPicker组件
1.组件代码
/* eslint-disable no-undef */
import { Component } from "react";
import { View, Text, Image } from "@tarojs/components";
import { AtImagePicker, AtMessage, AtToast } from "taro-ui";
import Taro, { getCurrentInstance } from "@tarojs/taro";
import "./index.scss";
import service from "@/services/index";
import { createFormData } from "@/utils/util";
class ImgPicker extends Component {
constructor() {
super();
this.state = {
files: []
};
}
getCaption(str, code) {//截取某字符后的字符串
var index = str.lastIndexOf(code);
str = str.substring(index + 1, str.length);
return str;
}
fileToUrl = files => {
if (!files || !files?.length) return [];
let temp = files.map(item => {
return item.url;
});
return temp;
};
urlToFile = urls => {
if (!urls || !urls?.length) return [];
let temp = urls.map(url => {
let item = { url };
return item;
});
return temp;
};
componentDidMount() {
const { files } = this.props;
if (files) this.setState({ files: this.urlToFile(files) });
}
componentWillUpdate(nextProps, nextState) {
// if (JSON.stringify(nextProps.files) !== JSON.stringify(this.props.files)) {
// this.setState({ files:this.urlToFile(nextProps.files) });
// }
}
onChange = async imgs => {
const { files } = this.state;
const { onChange } = this.props;
if (imgs?.length > files?.length) {
//上传图片
let uploadUrl = await this.uploadImg(imgs[imgs?.length - 1]);
if (uploadUrl) {
let temp = files;
let item = {
url: uploadUrl,
file: {
path: uploadUrl
}
};
temp.push(item);
this.setState(
(preveState, preveProps) => ({
files: temp
}),
() => {
onChange && onChange(this.fileToUrl(files));
}
);
}
} else {
//删除图片
this.setState({ files: imgs });
onChange && onChange(this.fileToUrl(imgs));
}
};
uploadImg = async file => {
let res = "";
Taro.showLoading({ title: "文件上传中..." });
let type = getCaption(file.url, ".");
let fileUrl = {
uri: file.url,
type: "image/" + (type === "jpg" ? "jpeg" : type),
name: ""
};
let formData = createFormData({
bucketName: "public",
fileName: new Date().getTime() + "." + type,
file: fileUrl
});
res = await service.upload(formData);
Taro.hideLoading();
if (res?.failed) {
Taro.hideLoading();
Taro.atMessage({
message: `上传失败,${res?.message}`,
type: "error"
});
} else {
Taro.atMessage({
message: `上传成功`,
type: "success"
});
}
return res;
};
render() {
const { maxLength } = this.props;
const { files } = this.state;
return (
<View className={"imgPicker " + this.props.className}>
<AtImagePicker
length={2}
mode="aspectFill"
{...this.props}
multiple={false}
count={1}//调用的后端接口不允许多选
// count={maxLength - files?.length}
showAddBtn={files?.length !== maxLength}
files={files}
onChange={e => this.onChange(e)}
onFail={e => console.log("fail", e)}
onImageClick={(i, file) => {//点击预览
Taro.previewImage({
current: file?.url,
urls: this.fileToUrl(files)
});
}}
/>
</View>
);
}
}
ImgPicker.defaultProps = {
maxLength: 10,
files: []
};
export default ImgPicker;
2.组件使用
<ImgPicker
maxLength={6}
files={this.state.files}
onChange={files => {
this.setState({ files });
}}
/>
其他上传方式
//其他方法如上
uploadImg = async file => {
Taro.showLoading({ title: "文件上传中..." });
let type = getCaption(file.url, ".");
let fileUrl = {
uri: file.url,
type: "image/" + (type === "jpg" ? "jpeg" : type),
name: ""
};
let formData = {
bucketName: "public",
fileName: getCaption(file.url, "/"),
file: fileUrl
};
const uploadTask = await Taro.uploadFile({
url: apiConfig.baseUrl + `/test/v1/files/multipart`, //仅为示例,非真实的接口地址
filePath: file.url,
name: "file",
formData: formData,
header: {
Authorization: "Bearer " + Taro.getStorageSync("TOKEN"),
"content-type": `multipart/form-data;charset=UTF-8;boundary=${boundary}`
},
success: function(result) {
Taro.hideLoading();
let res = result.data;
if (res?.failed || res?.error) {
Taro.hideLoading();
Taro.atMessage({
message: `上传失败,${res?.message || "未知错误"}`,
type: "error"
});
} else {
Taro.atMessage({
message: `上传成功`,
type: "success"
});
}
}
});
return uploadTask.data;
};