React之image图片格式互相转化

一、函数

(1)图片绝对路径转文件流File

路径就是类似下面这种


"/pic/userpic/4594b300f6b33903d172d71c6c884dde_1614841699937034945.jpeg"

1. 主函数

export const urlToFile = (url: string): Promise<any> => {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.crossOrigin = '';
    image.src = url;
    image.onload = function(): void {
    //转base64
      const base64ImageSrc = getBase64Image(image);
      //base64转file
      const file = dataURLtoFile(base64ImageSrc, 'image'); 
      resolve(file);
    };
    image.onerror = (e): void => reject(e);
  });
};

2. 获取图片base64

export function getBase64Image(img: any): string {
  const canvas = document.createElement('canvas');
  canvas.width = img.width;
  canvas.height = img.height;
  const ctx = canvas.getContext('2d');
  if (!ctx) return '';
  ctx.drawImage(img, 0, 0, img.width, img.height);
  const ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
  const dataURL = canvas.toDataURL('image/' + ext);
  return dataURL;
}

3. base64转file

export function dataURLtoFile(dataurl: string, filename: string): any {
  // 获取到base64编码
  const arr = dataurl.split(',');
  // 将base64编码转为字符串
  const bstr = window.atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n); // 创建初始化为0的,包含length个元素的无符号整型数组
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, {
    type: 'image/jpeg',
  });
}

二、页面结构(最终图片以file格式提交给后端)即formdata

(1)html页面结构显示

1. 主页面

<Item label={'头像设置'}>
  {getFieldDecorator('image', {
    rules: [{ required: true, message: '请上传头像' }],
  })(<UploadImg size={'default'} />)}
</Item>

2. 子页面

import React, { FC, forwardRef, useState, memo, useEffect } 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?(file: any): void;
  //传递base64时,onChange?(image: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 [img, setImg] = useState(''); //使用变量实现预览图片

  const uploadProps = {
    accept: 'image/*',
    beforeUpload(): boolean {
      setLoading(true);
      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) {
        //获取base64
          getBase64(info.file, async (imgUrl: any) => {
            if (onChange) {
              //onChange(imgUrl as string); //可以传递base64
              setImg(imgUrl);
              onChange(info.file);//传递file对象
            }
          });
        } else {
        //没有自带旋转则旋转该图片
          const result = await getBase64Rotatatin(info.file);
          if (onChange) {
            //onChange(result as string);
            setImg(result as string);
            onChange(info.file);
          }
        }
      } catch (e) {
        console.error(e);
      } finally {
        setLoading(false);
      }
    },
    onRemove(): boolean {
      if (onChange) {
        onChange('');
      }
      return true;
    },
  };
  useEffect(() => {
    if (typeof value === 'string') {
      setImg(value);
    }
  }, [value]);
  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={'上传中...'}>
        {img ? (
          <div className={styles['upload-img__preview']}>
            <img src={img} style={{ width: '100%' }} />
          </div>
        ) : (
          uploadButton
        )}
      </Spin>
    </AntdUpload>
  );
};

export default memo(forwardRef(UploadImage));

(2) 判断浏览器是否自带复原函数

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);
    }
  });
}

(3)图片旋转函数

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);
    });
  });
};

三、页面使用

(1)不涉及图片回显

不涉及图片回显的情况直接调用上面的代码即可
当表单提交,通过value即可拿到数据

 const handleSubmit: React.FormEventHandler<HTMLFormElement> = useCallback(
    (e): void => {
      e.preventDefault();
      validateFields(async (err, values) => {
        if (!err) {
          try {
            const formData = new FormData();
            formData.append('image', values.image);;
            shangchuantupian(formData);
            history.push('/xxx');
              message.success('添加成功');
          } catch (e) {
            message.error(e.message || '添加失败');
          }
        }
      });
    },
    [validateFields],
  );

(2)涉及图片回显(回显格式为路径)

const handleSubmit: React.FormEventHandler<HTMLFormElement> = useCallback(
    (e): void => {
      e.preventDefault();
      validateFields(async (err, values) => {
        if (!err) {
          try {
            const query = queryString.parse(location.search);
            const { image = '' } = values;
            let img_file = image;
            if (typeof image === 'string') {
              //路径,调用上面的函数
              img_file = await urlToFile(image);
            }
            const formData = new FormData();
            formData.append('image', img_file);
            if (query && query.id) {
              formData.append('id', query.id as string);
              editToFile(formData);
              history.push('/xxx');
              message.success('更新成功');
            }
          } catch (e) {
            message.error(e.message || '更新失败');
          }
        }
      });
    },
    [validateFields, location.search],
  );

四、后端接口

(1)后端上传接口

export async function editToFile(params: any): Promise<ResponseReturn<PliliangrukuResponse>> {
  return await http.post('/users/modify', params, {
    headers: {
      'Content-Type': 'multipart/form-data',//重写请求头
    },
  });
}

(2)axios封装http

export const http = axios.create({
  baseURL: '/xxx',
  timeout: 100000,
  responseType: 'json',
  headers: { 'Content-Type': 'application/json' },//可有可无
});

参考文章:https://my.oschina.net/u/4280335/blog/3387307
https://blog.csdn.net/yin13037173186/article/details/83302628

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值