首先去阿里云OSS控制台,创建bucket账号,得到accessKeyId、accessKeySecret
egg项目配置OSS
下载egg-oss
npm i egg-oss --save-dev
配置oss
//config/config.default.js
config.oss = {
client: {
bucket: 'bucket',//创建bucket自定义的账号
region: 'oss-cn-hangzhou',
endpoint: "oss-cn-hangzhou.aliyuncs.com",
accessKeyId: 'accessKeyId',//创建bucket生成的accessKeyId
accessKeySecret: 'accessKeyId',//创建bucket生成的accessKeySecret
secure: true,
},
};
// config/plugin.js
oss: {
enable: true,
package: 'egg-oss',
}
生成接口
controller下新建upload.js文件
// app/controller/upload.js
'use strict';
const path = require('path');
const Controller = require('egg').Controller;
class UploadController extends Controller {
/**
* 上传文件
*/
async uploadAvatar() {
const { ctx } = this;
let filePath;
const stream = await ctx.getFileStream();
if (stream.fields.filePath) {
filePath = stream.fields.filePath + '/' + path.basename(stream.filename);
} else {
filePath = path.basename(stream.filename);
}
const result = await ctx.oss.put(filePath, stream);
ctx.body = {
data:{
code: 200,
message: '上传成功',
data: { url: result.url },
}
};
}
}
module.exports = UploadController;
配置接口路由
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller, middleware } = app;
router.post('/upload/uploadAvatar', controller.upload.uploadAvatar);
};
这样我们就得到一个接口,名为:/upload/uploadAvatar.接下来让我们使用它
React项目调用接口
封装图片上传组件
import React, { useState, forwardRef, useImperativeHandle, useEffect } from 'react'
import { PlusOutlined } from '@ant-design/icons';
import { Modal, Upload } from 'antd';
// import type { UploadChangeParam } from 'antd/es/upload';
import type { RcFile, UploadFile, UploadProps } from 'antd/es/upload/interface';
import '../index.less'
const getBase64 = (file: RcFile): Promise<string> =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
const JsImageUpload = ( props:any, ref:any ) => {
const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState('');
const [previewTitle, setPreviewTitle] = useState('');
const [fileList, setFileList] = useState<UploadFile[]>([])
const imgUrl = props?.urlData
// console.log(imgUrl)
useEffect(() => {
if(imgUrl) {
let obj = {
uid: Math.floor(Math.random()*100)+"",
name: Math.floor(Math.random()*100)+'',
url: imgUrl
}
setFileList([obj])
}
}, [imgUrl])
//解决异步回调的问题
useEffect(() => {
}, [fileList]);
// 可以让父组件调用子组件的方法
// 作用: 减少父组件获取子组件的DOM元素属性,只暴露给父组件需要用到的DOM方法
// 参数1: 父组件传递的ref属性
// 参数2: 返回一个对象,父组件通过ref.current调用对象中方法
useImperativeHandle(ref, () => ({
}));
const handleCancel = () => setPreviewOpen(false);
const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj as RcFile);
}
setPreviewImage(file.url || (file.preview as string));
setPreviewOpen(true);
setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1));
};
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
setFileList(newFileList);
props.uploadImgData(newFileList, props.urlIdx)
}
const uploadButton = (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>产品图片上传</div>
</div>
);
return (
<div className="upLoadPictureItem">
<Upload
action="http://127.0.0.1:7001/upload/uploadAvatar"
listType="picture-card"
fileList={fileList}
onPreview={handlePreview}
onChange={handleChange}
>
{fileList.length >= 1 ? null : uploadButton}
</Upload>
<Modal open={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
)
}
export default forwardRef(JsImageUpload)
前端父组件调用
import JsImageUpload from '@/components/content/jsImageUpload';
import React, { useRef, useState, forwardRef, useImperativeHandle } from 'react';
import { Button, Form, Input, InputNumber, message, Modal, Popover, Tree, Radio, Breadcrumb, Card, Select, } from 'antd';
import { useSelector, shallowEqual } from "react-redux";
import axios from '@/services/index'
import { CloseCircleOutlined, PlusOutlined } from '@ant-design/icons';
import './index.less'
import JsImageUpload from '@/components/content/jsImageUpload';
import { deepCopy } from '@/utils/devUtils';
import { useSearchParams, useLocation, useNavigate } from 'react-router-dom';
//图片上传父子组件交互使用
const ProductContent = ( props:any, ref:any ) => {
let imgMethodRef = useRef<any>(null);
// 可以让父组件调用子组件的方法
// 作用: 减少父组件获取子组件的DOM元素属性,只暴露给父组件需要用到的DOM方法
// 参数1: 父组件传递的ref属性
// 参数2: 返回一个对象,父组件通过ref.current调用对象中方法
useImperativeHandle(ref, () => ({
}));
//获取图片上传子组件url
const uploadImgData = (urlArr:any, idx:any)=>{
const arr = deepCopy(detail)//深拷贝方法
const urlObj = urlArr[0]
if(urlObj && urlObj.status == "done" && urlObj.response.data.code == 0) {
const url = urlObj.response.data.data.url
arr[idx].url = url
setDetail(arr)
}
}
<Form.Item
label=""
>
{detail.map((item, index) => (
<Card title=""
key={item.id}
extra={
detail && detail.length > 1 &&
<a href="#"
onClick={(index:any)=>deleteRowDetail(index)}>
<CloseCircleOutlined /></a>}
style={{ width: 600 }}>
<div className="card">
<div>
<JsImageUpload
uploadImgData={uploadImgData}
urlData={item.url} //图片回显
urlIdx={index} //当前图片在的下标用于保存
ref={imgMethodRef} />
</div>
</div>
</Card>
))}
</Form.Item>
})
export default forwardRef(ProductContent)