React+umi+antdesign实现上传文件组件(阿里云)

开始之前需要封装一个上传的方法fileUtils.ts

import {
    message } from 'antd';
import Base64 from 'crypto-js/enc-base64';
import Utf8 from 'crypto-js/enc-utf8';
import HmacSHA1 from 'crypto-js/hmac-sha1';
import {
    request } from 'umi';

const isDev = process.env.NODE_ENV === 'development';

export namespace FileUtil {
   
  const env = {
   
    timeout: 10000,
    uploadHost: 'https://hopeman.oss-cn-beijing.aliyuncs.com/',
  };

  const genPolicy = () => {
   
    const date = new Date();
    date.setHours(date.getHours() + env.timeout);
    const srcT = date.toISOString();
    const policyText = {
   
      expiration: srcT,
      conditions: [
        ['content-length-range', 0, 1 * 1024 * 1024 * 1024], // 设置上传文件的大小限制1G
      ],
    };
    const rawStr = JSON.stringify(policyText);
    const wordArray = Utf8.parse(rawStr);
    const policyBase64 = Base64.stringify(wordArray);
    return policyBase64;
  };

  const genSignature = (policyBase64: string, accessKey: string) => {
   
    const byte = HmacSHA1(policyBase64, accessKey);
    const signature = Base64.stringify(byte);
    return signature;
  };

  export const upload = async (options: any, dir?: string): Promise<string> => {
   
    const fileInfo = options.file;
    const {
    onError = () => {
   }, onSuccess = () => {
   }, fullHost = '', ext = false } = options;

    return new Promise(async (resolve) => {
   
      const res = await request('/common/ram/assumeRole', {
   
        method: 'POST',
      });

      if (res?.code === 200 && res?.data) {
   
        const {
   
          Credentials: {
    AccessKeyId, AccessKeySecret, SecurityToken },
        } = res.data;

        const fileNameSplit = fileInfo?.name.split('.') || [];
        const aliyunFileKey = `${
     dir ?? 'excel'}/event_${
     new Date().getTime()}${
     
          ext ? '.' + fileNameSplit[fileNameSplit?.length - 1] : ''
        }`; //文件命名

        const policyBase64 = genPolicy();
        const signature = genSignature(policyBase64, AccessKeySecret);

        const data = new FormData();
        data.append('key', aliyunFileKey);
        data.append('policy', policyBase64);
        data.append('OSSAccessKeyId', AccessKeyId);
        data.append('signature', signature);
        data.append('x-oss-security-token', SecurityToken);
        data.append('file', fileInfo);
        data.append('success_action_status', '200');

        try {
   
          await request(isDev ? '/upload' : UPLOAD_HOST, {
   
            method: 'POST',
            data,
          });
          const url = `${
     fullHost}${
     aliyunFileKey}`;
          onSuccess(url);
          resolve(url);
        } catch (e) {
   
          onError(options);
          resolve('');
        }
      } else {
   
        message.error('上传失败');
        onError(options);
        resolve('');
      }
    });
  };

  export const base64CoverFile = (dataUrl: string) => {
   
    /**
     * base64 转 File
     * @param data
     */
    const arr = dataUrl.split(','),
      mime = arr?.[0]?.match(/:(.*?);/)?.[1],
      bstr = atob(arr[1]);

    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
   
      u8arr[n] = bstr.charCodeAt(n);
    }
    const blob = new Blob([u8arr], {
    type: mime });
    // blob.lastModifiedDate = new Date(); // 文件最后的修改日期
    // blob.name = fileName; // 文件名
    return new File([blob], Date.now().toString(), {
    type: blob.type, lastModified: Date.now() });
  };
  // 浏览器本页下载文件
  export const downFileByUrl = (url: string) => {
   
    if (!url || url.length === 0) {
   
      message.error('url 不存在');
      return;
    }
    const a = document.createElement('a');
    a.setAttribute('href', url);
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };

  export const downMultipleFileByUrl = (urls: string[]) => {
   
    if (!urls || urls.length === 0) {
   
      message.error('url 不存在');
      return;
    }
    for (let i = 0; i < urls.length; i++) {
   
      const iframe = document.createElement('iframe');
      iframe.style.display = 'none'; // 防止影响页面
      // @ts-ignore
      iframe.style.height = 0; // 防止影响页面
      iframe.src = urls[i];
      document.body.appendChild(iframe);
      // 5分钟之后删除
      setTimeout(() => {
   
        iframe.remove();
      }, 5 * 60 * 1000);
    }
  };
}

//AliyunOssUpload 组件
import {
    FileUtil } from '**/fileUtils';
import {
    PlusOutlined, UploadOutlined } from '@ant-design/icons';
import type {
    UploadProps } from 'antd';
import {
    Button, Image, message, Upload } from 'antd';
import type {
    RcFile } from 'antd/lib/upload';
import type {
    UploadFile } from 'antd/lib/upload/interface';
import filesize from 'filesize';
import type {
    FC, ReactElement } from 'react';
import React, {
    Fragment, useState } from 'react';
import styles from './styles.less';

export type AliyunOssUploadProps = UploadProps & {
   
  value?: any;
  fullHost?: string | boolean; // 阿里云的Host,默认只返回目录,当 true 时,默认的是 UPLOAD_HOST
  limitSize?: number; // 文件大小限制单位 bytes(B) 默认是 2 * 1024 * 1024(2M) 0 表示不限制
  ext?: boolean; // 阿里云文件路径中是否包含文类后续名, 默认不包含文件类型后缀
  buttonRef?: any;
  readOnly?: boolean;
  uploadButtonRender?: ReactElement;
  maxCount?: number;
};

const UploadImageButton = React.forwardRef((props, ref: any) => {
   
  return (
    <div ref={
   ref} className={
   'aliyunUpBtn'}>
      <PlusOutlined className={
   'aliyunUpIcon'} />
      <div className={
   'aliyunUpTitle'} style={
   {
    marginTop: 8 }}>
        上传图片
      </div>
    </div>
  );
});

const UploadNormalButton = React.forwardRef((props, ref: any) => (
  <Button ref={
   ref} icon={
   <UploadOutlined />}>
    点击上传
  </Button>
));

const AliyunOssUpload: FC<AliyunOssUploadProps> = (props) => {
   
  const [previewVisible, setPreviewVisible] = useState(false);
  const [previewSrc, setPreviewSrc] = useState('');
  let {
    fullHost = '' } = props;
  const {
   
    ext = false,
    disabled,
    buttonRef,
    readOnly = false,
    maxCount = 1,
    uploadButtonRender,
  } = props;

  fullHost = typeof fullHost === 'boolean' ? (fullHost ? UPLOAD_HOST : '') : fullHost;

  const uploadProps: UploadProps = {
   
    listType: 'picture-card',
    headers: {
   
      authorization: 'authorization-text',
    },
    maxCount: maxCount,
    customRequest: (options) => FileUtil.upload({
    ...options, fullHost, ext }),
    onPreview: (file: UploadFile) => {
   
      const src = file?.response || file?.thumbUrl;
      if (src) {
   
        setPreviewSrc(src);
        setPreviewVisible(true);
      }
    },
    defaultFileList: props?.value?.fileList?.map((item: any) => ({
   
      uid: item?.uid,
      name: item?.url,
      url: item?.url,
      response: item?.url,
      thumbUrl: item?.url,
    })),
    beforeUpload: async (file: RcFile) => {
   
      const initAccept = props?.accept || 'image/png,image/jpg,image/jpeg';
      const accept = initAccept.split(',');

      const limitSize = props?.limitSize ?? 1024 * 1024 * 2;
      const isValidType = accept?.includes(file.type);
      const isValidSize = limitSize ? file.size < limitSize : true;
      if (!isValidType) {
   
        await message.error(`仅支持 ${
     initAccept} 格式`);
      }
      if (!isValidSize) {
   
        await message.error(
          `图片大小不能超过 ${
     filesize(limitSize, {
      base: 2, standard: 'jedec' })}`,
        );
      }
      return (isValidType && isValidSize) || Upload.LIST_IGNORE;
    },
    showUploadList: {
    showRemoveIcon: !readOnly },
    ...props,
  };

  const uploadButton = () => {
   
    if (readOnly || disabled) {
   
      return;
    }
    if (uploadProps?.maxCount) {
   
      if (props?.value?.fileList?.length >= uploadProps.maxCount) {
   
        return null;
      } else {
   
        if (uploadButtonRender) return uploadButtonRender;
        return props.listType === 'picture-card' || 'picture' ? (
          <UploadImageButton ref={
   buttonRef} />
        ) : (
          <UploadNormalButton ref={
   buttonRef} />
        );
      }
    } else {
   
      if (uploadButtonRender) return uploadButtonRender;
      return props.listType === 'picture-card' || 'picture' ? (
        <UploadImageButton ref={
   buttonRef} />
      ) : (
        <UploadNormalButton ref={
   buttonRef} />
      );
    }
  };

  if (!props.value?.fileList?.length && disabled) return <>-</>;

  return (
    <Fragment>
      <Upload className={
   styles.uploadBox} {
   ...uploadProps}>
        {
   uploadButton()}
      </Upload>
      <div style={
   {
    fontSize: 0, position: 'absolute' }}>
        <Image
          width={
   200}
          style={
   {
    display: 'none' }}
          src={
   previewSrc}
          preview={
   {
   
            visible: previewVisible,
            src: previewSrc,
            onVisibleChange: (val) => {
   
              setPreviewVisible(val);
            },
          }}
        />
      </div>
    </Fragment>
  );
};

export default AliyunOssUpload;

在页面中使用 时

<AliyunOssUpload
	accept={
   'image/png,image/jpg,image/jpeg'}
	fullHost
	maxCount={
   5} //根据业务需求填写
	listType={
   'picture-card'}
	limitSize={
   1024 * 1024 * 5} //根据业务需求填写
 />

相关推荐

  1. React+umi+antdesign实现文件组件阿里

    2023-12-26 06:56:02       33 阅读
  2. 阿里文件

    2023-12-26 06:56:02       35 阅读
  3. 【SpringBoot实战】基于阿里实现文件

    2023-12-26 06:56:02       38 阅读
  4. 【SpringBoot】文件阿里

    2023-12-26 06:56:02       37 阅读
  5. 文件阿里

    2023-12-26 06:56:02       14 阅读
  6. PHP实现阿里OSS文件

    2023-12-26 06:56:02       34 阅读
  7. mac本地文件到远程阿里

    2023-12-26 06:56:02       23 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-26 06:56:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-26 06:56:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-26 06:56:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-26 06:56:02       18 阅读

热门阅读

  1. Python:GUI & Tkinter

    2023-12-26 06:56:02       35 阅读
  2. 第二百二十八回

    2023-12-26 06:56:02       35 阅读
  3. Microsoft Edge的详细使用方法和心得

    2023-12-26 06:56:02       35 阅读
  4. 一个WebSocket的自定义hook

    2023-12-26 06:56:02       36 阅读
  5. c语言之求出某年某月的天数

    2023-12-26 06:56:02       39 阅读
  6. 【云原生、k8s】管理Kubernetes应用搭建与部署

    2023-12-26 06:56:02       40 阅读