Ant Design Upload 组件上传图片到 Golang 服务端

更新日期: 2021-05-29 阅读次数: 2940 字数: 1085 分类: ReactJS

上传组件

Antd Pro 中使用 Ant Design 的 Upload 上传组件

https://ant.design/components/upload-cn

图片预览还是有必要的,所以先参考“图片墙”那个示例代码。

封装!解决状态无法重置的问题

简单将示例中的代码复制到了 Ant Design Form 中,但是在 Antd Pro Table List 中切换数据行时,会发现图片预览一直是上一个已上传的图片。即,组件状态没有重置。

看了网上大哥们的实现,我发现都是将 Upload 再封装为一个组件。 然后在 Form Item 中使用该组件。 这样遵循 Form Item 的规范,就可以做到像 Form Item 中使用 Input 组件一样方便了。

而且,封装成独立组件之后,React 的组件复用优势就体现出来了。

公共复用组件的复用方法

公共组件的放置位置:

> tree src/components/
src/components/
├── Footer
│   └── index.tsx
├── HeaderDropdown
│   ├── index.less
│   └── index.tsx
├── HeaderSearch
│   ├── index.less
│   └── index.tsx
├── NoticeIcon
│   ├── NoticeIcon.tsx
│   ├── NoticeList.less
│   ├── NoticeList.tsx
│   ├── index.less
│   └── index.tsx
├── RightContent
│   ├── AvatarDropdown.tsx
│   ├── index.less
│   └── index.tsx
└── index.md

引入示例:

src/pages/user/Login/index.tsx

import Footer from '@/components/Footer';

复制了照片墙的代码到组件中,目前看一切正常。

向 golang 服务上传图片

如何配置服务端的域名?

其实不需要担心这个问题,因为生产环境前端和后端通常在一个服务器上,所以直接写 url 后缀即可。

<Upload
    action="/api/upload-image"

此时,会看到后台报了 404 错误,说明请求已经到了后台

[GIN] 2021/05/27 - 16:42:51 | 404 | 500ns | 127.0.0.1 | POST "/api/upload-image"

Golang 的后台处理

Goland Gin 将上传的图片文件转存到七牛 CDN

前端处理返回的图片链接

通过 onChange 的回调返回数据,来获取服务端的返回数据。参考:

https://ant.design/components/upload-cn/#onChange

判断 file 的 status, 如果为 done,说明上传成功。 再从 response 中读取服务端的返回。

lastModified: 1621317890531
lastModifiedDate: Tue May 18 2021 14:04:50 GMT+0800 (中国标准时间) {}
name: "可爱.png"
originFileObj: File {uid: "rc-upload-1622172406587-18", name: "可爱.png", lastModified: 1621317890531, lastModifiedDate: Tue May 18 2021 14:04:50 GMT+0800 (中国标准时间), webkitRelativePath: "", …}
percent: 100
response: {data: "http://wx.sunzhongwei.com/some_app/6d9f1cf9-4189-433c-90f7-3763b32e2805", err_code: 0, err_msg: "OK"}
size: 5991
status: "done"
thumbUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMg
type: "image/png"
uid: "rc-upload-1622172406587-18"
xhr: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
__proto__: Object

Upload 组件如何将数据返回给 Form 呢?

参考

https://anandzhang.com/posts/frontend/14

简言之就是在 Upload 子组件的 handleChange 中调用父组件的 onChange 方法。即

this.props.onChange(fileList)

这样就能将数据传递给父组件。

如果是想只将图片的 URL 返回父组件,只需要将 fileList 替换为 URL 即可。

同时,不要忘了,在 Upload 子组件初始化时,将默认值 (this.props.value)设置好。

果然可以了。

子组件修改父组件传递的 props

不能直接修改 props 中的值,因为会报错说 props 中字段是只读的。

正确的做法是,通过 props 中的 onChange 这种回调函数来修改父组件中的状态。

完整代码

import React from 'react';
import { Upload, Modal, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';

function getBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = error => reject(error);
  });
}

class PicturesWall extends React.Component {
  constructor(props) {
    super(props);
    let _state = {
      previewVisible: false,
      previewImage: '',
      previewTitle: '',
      cover_img: this.props.value,
      fileList: [],
    };

    if (this.props.value) {
      _state.fileList = [
        {
          uid: '-1',
          name: '预览图',
          status: 'done',
          url: this.props.value,
        },
      ];
    }

    this.state = _state;
  }


  handleCancel = () => this.setState({ previewVisible: false });

  handlePreview = async file => {
    if (!file.url && !file.preview) {
      file.preview = await getBase64(file.originFileObj);
    }

    this.setState({
      previewImage: file.url || file.preview,
      previewVisible: true,
      previewTitle: file.name || file.url.substring(file.url.lastIndexOf('/') + 1),
    });
  };

  handleChange = ({ file, fileList, event }) => {
    const { status, response } = file
    if (status === 'done') {
      // 上传成功
      const { data, err_code } = response
      if (err_code == 0) {
        // 调用父组件 onChange 方法, 将新值传递回去
        this.props.onChange(data)
        message.success("图片上传成功")
      } else {
        message.error("图片上传失败")
      }
    }
    this.setState({ fileList })
  };

  render() {
    const { previewVisible, previewImage, fileList, previewTitle } = this.state;
    const uploadButton = (
      <div>
        <PlusOutlined />
        <div style={{ marginTop: 8 }}>上传</div>
      </div>
    );
    return (
      <>
        <Upload
          action="/api/upload-image"
          listType="picture-card"
          fileList={fileList}
          onPreview={this.handlePreview}
          onChange={this.handleChange}
        >
          {fileList.length >= 1 ? null : uploadButton}
        </Upload>
        <Modal
          visible={previewVisible}
          title={previewTitle}
          footer={null}
          onCancel={this.handleCancel}
        >
          <img alt="example" style={{ width: '100%' }} src={previewImage} />
        </Modal>
      </>
    );
  }
}

export default PicturesWall;

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式