OSS是什么
对象存储(Object Storage Service)指的是云存储服务。数据以对象(Object)的形式存储在OSS的存储空间(Bucket )中。如果要使用OSS存储数据,需要先创建Bucket,并指定Bucket的地域、访问权限、存储类型等属性。创建Bucket后,可以将数据以Object的形式上传到Bucket,并指定Object的文件名(Key)作为其唯一标识。
OSS以HTTP RESTful API的形式对外提供服务,访问不同地域需要不同的访问域名(Endpoint)。当您请求访问OSS时,OSS通过使用访问密钥(AccessKey ID和AccessKey Secret)对称加密的方法来验证某个请求的发送者身份。
简单易懂的解释就是把需要存储的非结构化数据(对象、视频、文本等)存储到阿里云的服务器上,其保证数据的可用性和安全性,获取到数据时直接调用接口即可。
使用Oss流程
创建bucket -> 选择传递方案 (我选择的是服务端签名直传)-> client get policy -> server return policy -> client post oss
官方文档如下:如何进行服务端签名直传_对象存储 OSS-阿里云帮助中心 (aliyun.com)
后端
-
nest.js编写接口:
tsx//pic_sign.controller.ts @Get() getSignature() { return this.picSignService.getSignature(); }
tsx//pic_sign.servic.ts async getSignature(): Promise<CreateOssSign> { const config = { //自己配制的key和secret accessKeyId:ACCESS_KEY, accessKeySecret:ACCESS_KEY_SECRET, bucket: //创建的bucker名, dir: //文件目录, } const client = new OSS(config) const date = new Date(); date.setDate(date.getDate() + 1); const policy = { expiration: date.toISOString(), // 请求有效期 conditions: [ ['content-length-range', 0, 1048576000], // 设置上传文件的大小限制 ], }; // bucket域名 const host = `http://${config.bucket}.${(await client.getBucketLocation()).location }.aliyuncs.com`.toString(); //签名 const formData = await client.calculatePostSignature(policy); //返回参数 const params = { expire: dayjs().add(1, 'days').unix().toString(), policy: formData.policy, signature: formData.Signature, accessId: formData.OSSAccessKeyId, host, dir: //传递文件的文件名, }; return params }
前端
- 直接调用接口获取到signature,可使用antd中自带的Upload组件,官方文档有针对于Oss上传方案的阐述,根据需求修改即可。
- 删除已上传的图片------官方文档DeleteMultipleObjects
遇到的bug:
- vite中直接使用crypto报错--crypto适用于node端,浏览器环境使用crypto-js
- 上传/删除oss成功后返回的状态码201 204,由于axios拦截器只拦截了200为成功,修改如下:
tsxconst successCode = [200, 201, 204]; axios.interceptors.response.use( (response) => { if (successCode.includes(response.data.statusCode as number)) { return response.data;
- modal不显示图片:拼接正确的url到组件中即可------修改file.url,添加oss.host前缀
用户状态管理
思路:原本想使用最近学的valtio,但发现valtio没办法实现持久化管理,于是转向localstroage,自己写了个hooks来管理登录后的用户状态
localStorage持久化管理
localstorage本质上是存储在浏览器当中的字符串,大小有5M左右,api也很简单,getItem,setItem,不过我们可以通过json.stringfy和json.parse将其进行对象和字符串的转化,具体实现如下 :
tsx
import { loginedData } from '&/types';
export const useUserInfo = () => {
const jsonString = localStorage.getItem('user');
const user: loginedData = (jsonString && JSON.parse(jsonString)) || '';
// 在localStorage中存储用户信息
const saveuser = (user: loginedData) => {
const userString = JSON.stringify(user);
localStorage.setItem('user', userString);
};
//修改用户信息
const updateuser = (newUser: loginedData['info']) => {
const preUser = JSON.parse(localStorage.getItem('user') || '');
const newUserString = JSON.stringify({ ...preUser, info: newUser });
localStorage.setItem('user', newUserString);
};
// 清空用户信息
const deleteuser = () => {
localStorage.removeItem('user');
};
return {
user,
saveuser,
deleteuser,
updateuser
};
};
使用:
tsx
const { deleteuser, user } = useUserInfo();
sessionStorage存储暂时信息
需求:当用户点击发布笔记时token刚好过期,存储当前用户编辑的内容,转到登录注册页面,等用户再次登录进来时自动填充刚刚所填写的内容
实现:使用sessionStorage(与localStorage差不多,只不过生命周期只在页面打开时保留,若页面关闭则随之清除)
tsx
const jsonString = sessionStorage.getItem('add');
const temBody = (jsonString && JSON.parse(jsonString)) || '';
//保留登录之前上传的图片
useEffect(() => {
if (temBody.pic) {
const temPicList: Array<UploadFile> = [];
const temPicArray = temBody.pic.split(',');
if (temPicArray.length === 1) {
temPicList.push({
uid: uuidv4(),
name: uuidv4(),
url: `${config.ossUrl}/${temBody.pic}`
});
} else {
temPicArray.map((item: string) =>
temPicList.push({
uid: uuidv4(),
name: uuidv4(),
url: `${config.ossUrl}/${item}`
})
);
}
setImgList(temPicList);
}
}, [temBody.pic]);
//handlesSubmit部分
//发布接口参数修改
const body: addNoteData = {
title: temBody ? temBody.title : title,
content: temBody ? temBody.content : context,
pic: temBody.pic
? temBody.pic
: imgList.map((item) => item.url).join(','),
avatar: user?.info.avatar,
user_id: user?.info.id
};
if (!user) {
//如登录过期,存储编辑后的信息,再次登录后自动填充
message.info('登录过期,请重新登录');
deleteuser();
sessionStorage.setItem('add', JSON.stringify(body));
naviagte('/auth');
return;
}
addNote(body);
//发布后清空表单、缓存
form.resetFields();
setImgList([]);
if (temBody) {
sessionStorage.removeItem('add');
}
message.success('发布成功');
//ui------from.item初始化值修改
<Form.Item
name={'title'}
rules={[{ required: true, message: '请填写您的笔记标题' }]}
initialValue={temBody?.title}
>
发布笔记
后端
需求:一个用户可以有多条笔记,一个笔记只能有一个用户,一对多多对一的关系
实现:在entity中关联
tsx
//notes
@Entity('notes')
export class Notes {
...
@ManyToOne(type => User, user => user.notes, { cascade: true })
user: User;
}
//users
@OneToMany(type=>Notes,notes=>notes.user)
notes:Notes[];
遇到的问题:
-
notes中pic栏原本设置的是传递数组,里面是pic的url,但是typeorm postgre不支持Array类型------Data type "Array" in "Notes.pic" is not supported by "postgres" database.
- 解决:使用字符串存储,前端传递过来用,拼接,
修改用户信息
需求:可以修改用户的用户名和头像
解决:编写patch接口,头像同样采用oss存储
遇到的问题:
-
修改user.username时使用
this.Repository.save()
方法没有在原来的数据的coloum修改而是新建了一个数据- 原因:username是主键,主键不可更改,使用typeorm.save执行的是插入操作
- 解决:重新引入nickname昵称字段,用户可以修改昵称