AntdPro上传文件之Antd各个版本的对比


之前一直在使用antd上传文件,图片,从antd3到antd4再到antdPro,总结一下经验吧

一、antd3上传图片

这里用的antd的版本是"antd": "^3.25.0",

import React, { FC, forwardRef, useState, memo } from 'react';
import { Button, Icon, Upload as AntdUpload, Spin } from 'antd';
import { UploadChangeParam } from 'antd/lib/upload';
import { UploadFile } from 'antd/lib/upload/interface';
import { getBase64Rotatatin } from 'utils';
import { detectImageAutomaticRotation } from 'utils/rotatain';
import styles from './uploadImg.module.scss';
import classNames from 'classnames';
interface Props {
  onChange?(base64: string): void;
  value?: string;
  size?: 'small' | 'default';
}
//获取base64
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function getBase64(img: any, callback: any) {
  const reader = new FileReader();
  reader.addEventListener('load', () => callback(reader.result));
  reader.readAsDataURL(img);
}
const UploadImage: FC<Props> = ({ onChange, value, size = 'default' }, ref): JSX.Element => {
  const [loading, setLoading] = useState(false);
  const uploadProps = {
    accept: 'image/*', //设置接收的文件时图片
    beforeUpload(): boolean {
      setLoading(true); //设置loading状态
      return false;
    },
    async onChange(info: UploadChangeParam<UploadFile<any>>): Promise<void> {
      if (!info.file) return Promise.reject(new Error('请选择图片'));
      try {
      //这个是我写的函数,判断系统是否自带回正
        const system = await detectImageAutomaticRotation();
        if (system) {
          getBase64(info.file, async (imgUrl: any) => {
            if (onChange) {
              onChange(imgUrl as string);
            }
          });
        } else {
        //回正函数
          const result = await getBase64Rotatatin(info.file);
          if (onChange) {
            onChange(result as string);
          }
        }
      } catch (e) {
        console.error(e);
      } finally {
        setLoading(false);
      }
    },
    onRemove(): boolean {
      if (onChange) {
        onChange('');
      }
      return true;
    },
  };
  const uploadButton = (
    <div className={styles['upload-img__btn']}>
      <Icon type={'camera'} className={styles['upload-img__icon']} />
      <Button>上传图片</Button>
    </div>
  );
  const className = classNames(styles['upload-img'], {
    [styles['upload-img--small']]: size === 'small',
  });
  return (
    <AntdUpload {...uploadProps} ref={ref} className={className} showUploadList={false}>
      <Spin spinning={loading} tip={'上传中...'}>
        {value ? (
          <div className={styles['upload-img__preview']}>
            <img src={value} style={{ width: '100%' }} />
          </div>
        ) : (
          uploadButton
        )}
      </Spin>
    </AntdUpload>
  );
};

export default memo(forwardRef(UploadImage));

//是否自带回正
const testAutoOrientationImageURL =
  'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' +
  'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' +
  'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' +
  'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' +
  'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' +
  'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==';
let isImageAutomaticRotation;

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function detectImageAutomaticRotation() {
  return new Promise(resolve => {
    if (isImageAutomaticRotation === undefined) {
      const img = new Image();

      // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
      img.onload = () => {
        // 如果图片变成 1x2,说明浏览器对图片进行了回正
        isImageAutomaticRotation = img.width === 1 && img.height === 2;

        resolve(isImageAutomaticRotation);
      };

      img.src = testAutoOrientationImageURL;
    } else {
      resolve(isImageAutomaticRotation);
    }
  });
}

//回正且转化函数
/* eslint-disable @typescript-eslint/no-explicit-any */
import EXIF from 'exif-js';
export const getOrientation = (file: any): Promise<number> => {
  return new Promise((resolve, reject) => {
    EXIF.getData(file, function() {
      try {
        EXIF.getAllTags(file);
        const orientation = EXIF.getTag(file, 'Orientation');
        resolve(orientation);
      } catch (e) {
        reject(e);
      }
    });
  });
};

export const setImgVertical = (imgSrc: string, orientation: number): Promise<string> => {
  return new Promise((resolve, reject) => {
    const image = new Image();
    if (!imgSrc) return;
    const type = imgSrc.split(';')[0].split(':')[0];
    const encoderOptions = 1;
    image.src = imgSrc;
    image.onload = function(): void {
      const imgWidth = (this as any).width;
      const imgHeight = (this as any).height;
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      if (!ctx) return;
      canvas.width = imgWidth;
      canvas.height = imgHeight;
      if (orientation && orientation !== 1) {
        switch (orientation) {
          case 6:
            canvas.width = imgHeight;
            canvas.height = imgWidth;
            ctx.rotate(Math.PI / 2);
            ctx.drawImage(this as any, 0, -imgHeight, imgWidth, imgHeight);
            break;
          case 3:
            ctx.rotate(Math.PI);
            ctx.drawImage(this as any, -imgWidth, -imgHeight, imgWidth, imgHeight);
            break;
          case 8:
            canvas.width = imgHeight;
            canvas.height = imgWidth;
            ctx.rotate((3 * Math.PI) / 2);
            ctx.drawImage(this as any, -imgWidth, 0, imgWidth, imgHeight);
            break;
        }
      } else {
        ctx.drawImage(this as any, 0, 0, imgWidth, imgHeight);
      }
      resolve(canvas.toDataURL(type, encoderOptions));
    };
    image.onerror = (e): void => reject(e);
  });
};
export const getBase64Rotatatin = (img: any): Promise<string | ArrayBuffer | null> => {
  return new Promise((resolve, reject) => {
    getOrientation(img).then(orientation => {
      const reader = new FileReader();
      reader.addEventListener(
        'load',
        async (): Promise<void> => {
          try {
            const base64 = await setImgVertical(reader.result as string, orientation);
            resolve(base64);
          } catch (e) {
            reject(e);
          }
        },
      );
      reader.addEventListener('error', () => reject(new Error('获取图片Base64失败')));
      reader.readAsDataURL(img);
    });
  });
};

后面就直接可以调用这个组件

const [image1, setImage1] = useState('');
const handleChange = useCallback((name: string) => {
    return (base64: string): void => {
      if (name === 'image1') {
        setImage1(base64);
      } else if (name === 'image2') {
        setImage2(base64);
      }
    };
  }, []);
  //handleChange 里的参数是用来辨别是哪个图片
<UploadImg value={image1} onChange={handleChange('image1')} />

二、antd4上传文件

这里用的antd4的版本是:"antd": "^4.13.1",

const [fileName, setFileName] = useState('');
//showUploadList={false} directory={false} multiple={false} 表示不能上传文件夹,不能上传多文件,不用显示list
<Upload {...uploadProps} showUploadList={false} directory={false} multiple={false}>
  <Button type="link" disabled={disable} className={styles['form-upload__link']}>
     //这里是想待文件上传成功后显示文件名
     {fileName ? fileName : '点击选择文件上传'}
   </Button>
 </Upload>
import { UploadChangeParam, UploadFile } from 'antd/lib/upload/interface';
const file_list = useRef<Blob>();
//定义的文件名检测数组
const FILE_NAME = [
    'xxx1.json',
    'xxx2.json',
  ];

  const uploadProps = {
    name: 'file', //设置上传的文件名
    beforeUpload(): boolean {
      return false;
    },
    async onChange(info: UploadChangeParam<UploadFile<any>>): Promise<void> {
      if (!info.file) return Promise.reject(new Error('请选择文件!'));
      try {
       //将上传的文件名赋值给name
        const name = info.file.name;
        //判断name是否符合自己的约束
        const flag = FILE_NAME.includes(name);  
        if (!flag) {
          message.error(
            `文件命名请按照以下规范:['xxx1.json',
              'xxx2.json']`,
          );
        } else {
        //file_list是自己定义的blob Ref ,将二进制文件赋给这个ref,需要注意的地方是二进制文件都需要formData的形式进行提交
          file_list.current = info.file.originFileObj;
          setFileName(name); //将文件名保存下来用于上面的组件回显
        }
      } catch (e) {
        console.error(e);
      }
    },
  };

比如说,我这里有个button进行提交。需要在form表单中使用我们上面写的组件,如下:

<Form onFinish={handleSubmit}>
    <Form.Item name="file_id">
        <Upload {...uploadProps} showUploadList={false} directory={false} multiple={false}>
          <Button type="link" disabled={disable} >
            {fileName ? fileName : '点击选择文件上传'}
          </Button>
        </Upload>
    </Form.Item>
  </div>
  <div>
    <Form.Item >
      <div style={{ paddingTop: 22 }}>
        <Button type={'primary'} disabled={disable} htmlType="submit">
          submit
        </Button>
      </div>
    </Form.Item>
</Form>

提交如下:

 const handleSubmit = useCallback(async () => {
   try {
      const formData = new FormData();
      //将刚刚赋值进去的blob 二进制文件流append到formData中
      formData.append('fileContent', file_list.current);
      //下面就是文件上传接口了,怎么写都行
      const { status } = await xxx( formData);
      if (status === 0) {
        message.success('文件下发成功!');
      } else {
        message.error('文件下发失败!');
      }
    } catch (e) {
      message.error(e || '出错了!');
    }
  }, [agent_info.uuid, fileName]);

三、使用antdPro

我这里的antd版本是:"antd": "^4.18.6", "@ant-design/pro-form": "^1.53.6",

import { get } from 'lodash-es';
<ProForm
  onFinish={async(values)=>{
    const { upload } = values;
    const formData: FormData = new FormData();
    fileList.current = get(upload[0], 'originFileObj');
    formData.append('FileName', fileList.current);
	try{
	//以下根据你自己的接口函数写
     await requestUpload({ data: formData });
     message.success("成功!")
	}catch(e){
	  message,error("失败!")
	}
  }}
  layout="horizontal"
>
 <ProFormUploadButton
   name="upload"
    label="上传文件"
    max={1}
    fieldProps={{
      name: 'file',
      beforeUpload(file): boolean {
        return get(file, 'size', 0) < 2 * (1024*1024);  //设置大小不能超过两mb
      },
      multiple: false,//不能多选
    }}
    accept=".xls,.xlsx" //设置文件类型,简直太方便了
    rules={[{ required: true, message: '请上传文件' },
    { //这里还可以使用自定义规则来对文件大小进行约束,超过2Mb就提交不了,更适合产品逻辑
      validator: (rule, [value]) =>
        new Promise<void>((resolve, reject) => {
          if (get(value, 'size', 0) < 2 * (1024 * 1024)) {
            resolve();
          } else {
            reject(new Error('单个文件不能超过2MB!'));
          }
        }),
    },]} //设置该标单项是必选
    extra={ //设置提示
      <span>
        支持格式:.xls .xlsx ,单个文件不能超过2MB。
      </span>
    }
  />
</ProForm>
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值