前端文件上传我建议是用 cors 直传,不要去代理转发,因为文件流转发 node 比较麻烦。
antd Upload 组件根据你提供的 api 将文件发送出去,然后提供返回结果的回调函数,你把返回结果传入 onChange 中交给表单提交。
import './style.less'
import { Avatar, Input, message, Upload } from 'antd'
import { UploadChangeParam } from 'antd/lib/upload'
import { UploadFile, UploadProps } from 'antd/lib/upload/interface'
import classnames from 'classnames'
import React, { useEffect, useState } from 'react'
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons'
import { Link } from '../link'
export interface ImgUpload {
className?: string
action?: string
value?: string
onChange?: (value: string) => void
multiple?: boolean
size?: [number, number]
}
function beforeUpload(file: File) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
if (!isJpgOrPng) {
message.error('只能上传 JPG/PNG 格式图片!')
}
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
message.error('图片大小必须小于 2MB!')
}
return isJpgOrPng && isLt2M
}
export const ImgUpload = ({
className,
action,
value,
onChange,
multiple = false,
size,
}: ImgUpload) => {
const [loading, setLoading] = useState(false)
const [imgUrl, setImgUrl] = useState<string>(value)
useEffect(() => {
setImgUrl(value)
}, [value])
const handleChange = (info: UploadChangeParam<UploadFile<any>>) => {
console.log('up-info', info)
if (info.file.status === 'uploading') {
setLoading(true)
return
}
if (info.file.status === 'done') {
setLoading(false)
const imageUrl = info?.file?.response?.url
setImgUrl(imageUrl)
onChange && onChange(imageUrl)
}
}
const uploadProps: UploadProps = {
action,
multiple,
beforeUpload,
onChange: handleChange,
}
return (
<Upload
listType="picture-card"
showUploadList={false}
className={classnames('ImgUpload', className)}
{...uploadProps}
>
{imgUrl ? (
<Avatar className="uploadedImg" shape="square" src={imgUrl} />
) : (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>上传图片</div>
{size && (
<div style={{ marginTop: 4 }}>
({size[0]}x{size[1]})
</div>
)}
</div>
)}
</Upload>
)
}
export const ImgUploadOrAdd = ({ value, onChange, ...props }: ImgUpload) => {
const [useUrl, setUseUrl] = useState(false)
let content = <></>
if (useUrl) {
content = (
<Input
placeholder="请输入图片url地址"
value={value}
onChange={e => onChange(e?.target?.value)}
/>
)
} else {
content = <ImgUpload {...props} value={value} onChange={onChange} />
}
return (
<div className="ImgUploadOrAdd">
{content}
<Link className="change" onClick={() => setUseUrl(!useUrl)}>
{useUrl ? '没有url?上传图片' : '设置已有图片url'}
</Link>
</div>
)
}
.ImgUpload {
.uploadedImg {
display: block;
width: 100%;
height: 100%;
img {
object-fit: contain;
}
}
}
.ImgUploadOrAdd {
.ant-upload-picture-card-wrapper {
width: auto;
.ant-upload-select {
margin: 0;
}
}
.change {
text-decoration: underline;
vertical-align: bottom;
margin-left: 16px;
}
}