整个组件功能代码-->图片压缩上传&&axios封装
// 项目中上传图片组件 and其它功能 import React, { useState, useRef } from 'react'; import { QrcodeOutlined } from '@ant-design/icons'; import '@ant-design/compatible/assets/index.css'; import { Modal, Button, message } from 'antd'; import { useObserver } from 'mobx-react'; import { useHistory, useLocation } from 'react-router-dom'; // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore import QRCode from 'qrcode'; import ajax from '@/lib/axios'; import { Wrapper, SuccessWrapper, UploadImgWrapper } from './link-qrcode-style'; interface IProps { merchantId: string; branchName: string; merchantName: string; personInCharge: string; } const LinkQrCode = (props: IProps) => { const [showConfirmModal, setShowConfirmModal] = useState<boolean>(false); const [showSuccessModal, setShowSuccessModal] = useState<boolean>(false); const [showUploadModal, setShowUploadModal] = useState<boolean>(false); const [uploadButtonLoading, setUploadButtonLoading] = useState<boolean>(false); const [confirmButtonLoading, setConfirmButtonLoading] = useState<boolean>(false); const [qrCodeUrl, setQrCodeUrl] = useState<string>(''); const [qrDecode, setQrDecode] = useState<string>(''); const [uploadImgUrl, setUploadImgUrl] = useState<string>(''); const [canvasToBlob, setCanvasToBlob] = useState<any>({}); const history = useHistory(); const location = useLocation(); const fileInputRef = useRef<HTMLInputElement>(null); // const ua = navigator.userAgent.toLowerCase(); // const isIos = ua.indexOf('iphone') !== -1 || ua.indexOf('ipad') !== -1; const uploadFileValidate = async () => { try { const makeBoundary = function() { return '----JQBoundary' + btoa(Math.random().toString()).substr(0, 12); }; const boundary = makeBoundary(); const formData = new FormData();
// 这里需要注意,formdata.append()括号里参数的名称是根据后端定的,不是固定的.
后端需要什么字端,就append什么字段,如果需要多个字段,就写多个append()
formData.append('qrcodeImage', canvasToBlob); const { data: { data }, } = await ajax.post('/qrcode/validate', formData, {
// 请求后端上传接口的方法中,参数直接是上面封装的formdata即可,不需要其它。
// 另外请求默认的content-type是application/json,上传文件接口这里需要自己定义content-type:multipart/form-data。
定义了content-type之后需要手动添加boundary,否则会报错。
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` }, }); setUploadButtonLoading(false); setShowUploadModal(false); if (data) { setQrDecode(data); setShowConfirmModal(true); } else { Modal.error({ content: "Sorry, we can't process this picture, please make sure you use a clear picture of valid QR code.", onOk() { const to = `${location.pathname}${location.search}`; history.push(to); }, }); } } catch (e) { message.error(e.message); } }; const qrCodeUploadedHandler = (imageFile: object, fileImageUrl: string) => { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d') as any; const img = new Image(); img.src = fileImageUrl; img.onload = () => { const originWidth = img.width; const originHeight = img.height; const maxWidth = 1280; const maxHeight = 768; let targetWidth = originWidth; let targetHeight = originHeight; if (originWidth > maxWidth || originHeight > maxHeight) { if (originWidth / originHeight > maxWidth / maxHeight) { // 更宽,按照宽度限定尺寸 targetWidth = maxWidth; targetHeight = Math.round(maxWidth * (originHeight / originWidth)); } else { targetHeight = maxHeight; targetWidth = Math.round(maxHeight * (originWidth / originHeight)); } } // canvas对图片进行缩放 canvas.width = targetWidth; canvas.height = targetHeight; // 清除画布 context.clearRect(0, 0, targetWidth, targetHeight); // 图片压缩 context.drawImage(img, 0, 0, targetWidth, targetHeight); canvas.toBlob( blob => { // const newImage = new File([blob as any], (imageFile as any).name, { type: (imageFile as any).type }); setCanvasToBlob(blob); }, 'image/jpeg', 0.7, ); }; img.onerror = () => console.error('Upload file of image format please.'); }; const handleUploadButton = (e: React.MouseEvent) => { e.preventDefault(); setUploadButtonLoading(true); uploadFileValidate(); }; const handleFileChange = (e: any) => { const file = e.target.files[0]; if (file !== undefined) { const fr = new FileReader(); fr.readAsDataURL(file); fr.addEventListener('load', () => { setUploadImgUrl(fr.result as string); //base64 qrCodeUploadedHandler(file, fr.result as string); setShowUploadModal(true); }); } else { setUploadImgUrl(''); } }; const backToListPage = () => { const to = `${location.pathname}${location.search}`; history.push(to); }; const handleLinkButton = (e: React.MouseEvent) => { e.preventDefault(); if (!!fileInputRef) { (fileInputRef as any).current.click(); } }; const handleConfirmLink = async (e: React.MouseEvent) => { e.preventDefault(); setConfirmButtonLoading(true); try { const { data: { data }, } = await ajax.post('/qrcode/bind', { qrcode: qrDecode, bindObjectId: props.merchantId }); if (data.length > 0) { try { const codeUrl = await QRCode.toDataURL(data[0]); setQrCodeUrl(codeUrl); } catch (err) { console.error(err); } setConfirmButtonLoading(false); setShowConfirmModal(false); setShowSuccessModal(true); } } catch (e) { message.error(e.message); } }; const handleConfirmModalCancel = (e: React.MouseEvent) => { e.preventDefault(); setShowConfirmModal(false); backToListPage(); }; const handleSuccessModalCancel = (e: React.MouseEvent) => { e.preventDefault(); setShowSuccessModal(false); backToListPage(); }; return useObserver(() => ( <> <div> <Button onClick={handleLinkButton} style={{ width: '100%', marginTop: 5 }} type="default" size="small"> <QrcodeOutlined /> Link QR code </Button> <input type="file" name="cover" accept="image/*" ref={fileInputRef} style={{ width: 0, height: 0 }} onChange={handleFileChange} /> </div> <Modal destroyOnClose={true} title="Confirm Information" visible={showConfirmModal} style={{ top: 20 }} width={'80%'} maskClosable={false} footer={[ <Button type="primary" key="confirm" style={{ fontSize: '30px', height: '60px' }} loading={confirmButtonLoading} htmlType="button" onClick={handleConfirmLink}> Confirm </Button>, <Button key="cancel" htmlType="button" style={{ fontSize: '30px', height: '60px' }} onClick={handleConfirmModalCancel}> Cancel </Button>, ]} onCancel={handleConfirmModalCancel}> <Wrapper> <p> {/* eslint-disable-next-line react/no-unescaped-entities */} You're linking the QR code you scaned to the following merchant. Please check the merchant information throughly. </p> <div className="confirm-information"> <div className="confirm-information-item"> <span>Merchant ID:</span> <p>{props.merchantId}</p> </div> <div className="confirm-information-item"> <span>Branch Name:</span> <p>{props.branchName}</p> </div> <div className="confirm-information-item"> <span>Merchant Name:</span> <p>{props.merchantName}</p> </div> <div className="confirm-information-item"> <span>Person in Charge:</span> <p>{props.personInCharge}</p> </div> </div> </Wrapper> </Modal> <Modal destroyOnClose={true} visible={showSuccessModal} style={{ top: 20 }} width={'50%'} maskClosable={false} footer={null} onCancel={handleSuccessModalCancel}> <SuccessWrapper> <p className="top-tip">QR code has been successfully linked!</p> <div className="branch-name"> <span>Branch Name:</span> <p>{props.branchName}</p> </div> <div className="qr-code"> <span>QR Code:</span> <img src={qrCodeUrl} alt="图片加载失败" /> </div> </SuccessWrapper> </Modal> <Modal destroyOnClose={true} visible={showUploadModal} style={{ top: 20, textAlign: 'center' }} width={'80%'} maskClosable={false} closable={false} footer={null}> <UploadImgWrapper> <img id="img" src={uploadImgUrl} style={{ width: '90%' }} alt="" /> </UploadImgWrapper> <Button type="primary" key="confirm" loading={uploadButtonLoading} size="large" htmlType="button" style={{ margin: '20px', width: '90%', fontSize: '30px', height: '60px' }} onClick={handleUploadButton}> UPLOAD </Button> </Modal> </> )); }; export default LinkQrCode;
// axios封装 import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, Canceler } from 'axios'; import Cookies from 'js-cookie'; import { message } from 'antd'; export let cancel!: Canceler; export const ROUTE_CHANGE = 'ROUTE_CHANGE'; export const handleError = (e: AxiosError) => { message.error(`CODE: ${e.code}\n MESSAGE: ${e.message}`); }; const requestInterceptor = (config: AxiosRequestConfig) => { const acceptLanguages = [...navigator.languages];
// config.headers['Authorization'] = xxx,这种方式写法只会重写 // headers中Authorization字段,不会重写整个headers. // 如果config.headers= {xxx},每次请求的headers都会被重写成这里定义 // 的,会导致你在调请求方法时手动指定的headers失效。
config.headers['Authorization'] = Cookies.get('token'); config.headers['Accept-Language'] = acceptLanguages.join(','); return config; }; const responseInterceptor = (res: AxiosResponse) => { if (res.data.code !== 'SUCCESS') { throw { isAxiosError: false, request: res.request, name: res.data.code, config: res.config, code: res.data.code, message: res.data.message, response: res, } as AxiosError; } else { return res; } }; const ajax = axios.create({ baseURL: '/api', timeout: 60000, }); ajax.interceptors.response.use(responseInterceptor); ajax.interceptors.request.use(requestInterceptor); export default ajax;
// AxiosRequestConfig ===> ts export interface AxiosTransformer { (data: any, headers?: any): any; } export interface AxiosAdapter { (config: AxiosRequestConfig): AxiosPromise<any>; } export interface AxiosBasicCredentials { username: string; password: string; } export interface AxiosProxyConfig { host: string; port: number; auth?: { username: string; password:string; }; protocol?: string; } export type Method = | 'get' | 'GET' | 'delete' | 'DELETE' | 'head' | 'HEAD' | 'options' | 'OPTIONS' | 'post' | 'POST' | 'put' | 'PUT' | 'patch' | 'PATCH' | 'link' | 'LINK' | 'unlink' | 'UNLINK' export type ResponseType = | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' export interface AxiosRequestConfig { url?: string; method?: Method; baseURL?: string; transformRequest?: AxiosTransformer | AxiosTransformer[]; transformResponse?: AxiosTransformer | AxiosTransformer[]; headers?: any; params?: any; paramsSerializer?: (params: any) => string; data?: any; timeout?: number; timeoutErrorMessage?: string; withCredentials?: boolean; adapter?: AxiosAdapter; auth?: AxiosBasicCredentials; responseType?: ResponseType; xsrfCookieName?: string; xsrfHeaderName?: string; onUploadProgress?: (progressEvent: any) => void; onDownloadProgress?: (progressEvent: any) => void; maxContentLength?: number; validateStatus?: (status: number) => boolean; maxRedirects?: number; socketPath?: string | null; httpAgent?: any; httpsAgent?: any; proxy?: AxiosProxyConfig | false; cancelToken?: CancelToken; } export interface AxiosResponse<T = any> { data: T; status: number; statusText: string; headers: any; config: AxiosRequestConfig; request?: any; } export interface AxiosError<T = any> extends Error { config: AxiosRequestConfig; code?: string; request?: any; response?: AxiosResponse<T>; isAxiosError: boolean; toJSON: () => object; } export interface AxiosPromise<T = any> extends Promise<AxiosResponse<T>> { } export interface CancelStatic { new (message?: string): Cancel; } export interface Cancel { message: string; } export interface Canceler { (message?: string): void; } export interface CancelTokenStatic { new (executor: (cancel: Canceler) => void): CancelToken; source(): CancelTokenSource; } export interface CancelToken { promise: Promise<Cancel>; reason?: Cancel; throwIfRequested(): void; } export interface CancelTokenSource { token: CancelToken; cancel: Canceler; } export interface AxiosInterceptorManager<V> { use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: any) => any): number; eject(id: number): void; } export interface AxiosInstance { (config: AxiosRequestConfig): AxiosPromise; (url: string, config?: AxiosRequestConfig): AxiosPromise; defaults: AxiosRequestConfig; interceptors: { request: AxiosInterceptorManager<AxiosRequestConfig>; response: AxiosInterceptorManager<AxiosResponse>; }; getUri(config?: AxiosRequestConfig): string; request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>; get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>; put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>; patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>; } export interface AxiosStatic extends AxiosInstance { create(config?: AxiosRequestConfig): AxiosInstance; Cancel: CancelStatic; CancelToken: CancelTokenStatic; isCancel(value: any): boolean; all<T>(values: (T | Promise<T>)[]): Promise<T[]>; spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R; } declare const Axios: AxiosStatic; export default Axios;