AntD框架的upload組件上傳圖片時遇到的一些坑

来源:https://www.cnblogs.com/qianguyihao/archive/2019/03/02/10460834.html
-Advertisement-
Play Games

前言 本次做後臺管理系統,採用的是 AntD 框架。涉及到圖片的上傳,用的是AntD的 "upload" 組件。 前端做文件上傳這個功能,是很有技術難度的。既然框架給我們提供好了,那就直接用唄。結果用的時候,發現 upload 組件的很多bug。下麵來列舉幾個。 備註:本文寫於2019 03 02, ...


前言

本次做後臺管理系統,採用的是 AntD 框架。涉及到圖片的上傳,用的是AntD的 upload 組件。

前端做文件上傳這個功能,是很有技術難度的。既然框架給我們提供好了,那就直接用唄。結果用的時候,發現 upload 組件的很多bug。下麵來列舉幾個。

備註:本文寫於2019-03-02,使用的 antd 版本是 3.13.6。

使用 AntD 的 upload 組件做圖片的上傳

因為需要上傳多張圖片,所以採用的是照片牆的形式。上傳成功後的界面如下:

(1)上傳中:

(2)上傳成功:

(3)圖片預覽:

按照官方提供的實例,特此整理出項目開發中的完整寫法,親測有效。代碼如下:

/* eslint-disable */

import { Upload, Icon, Modal, Form } from 'antd';

const FormItem = Form.Item;

class PicturesWall extends PureComponent {
  state = {
    previewVisible: false,
    previewImage: '',
    imgList: [],
  };


  handleChange = ({ file, fileList }) => {
    console.log(JSON.stringify(file)); // file 是當前正在上傳的 單個 img
    console.log(JSON.stringify(fileList)); // fileList 是已上傳的全部 img 列表

    this.setState({
      imgList: fileList,
    });
  };


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

  handlePreview = file => {
    this.setState({
      previewImage: file.url || file.thumbUrl,
      previewVisible: true,
    });
  };


  // 參考鏈接:https://www.jianshu.com/p/f356f050b3c9
  handleBeforeUpload = file => {
    //限製圖片 格式、size、解析度
    const isJPG = file.type === 'image/jpeg';
    const isJPEG = file.type === 'image/jpeg';
    const isGIF = file.type === 'image/gif';
    const isPNG = file.type === 'image/png';
    if (!(isJPG || isJPEG || isGIF || isPNG)) {
      Modal.error({
        title: '只能上傳JPG 、JPEG 、GIF、 PNG格式的圖片~',
      });
      return;
    }
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isLt2M) {
      Modal.error({
        title: '超過2M限制 不允許上傳~',
      });
      return;
    }
    return (isJPG || isJPEG || isGIF || isPNG) && isLt2M && this.checkImageWH(file);
  };

  //返回一個 promise:檢測通過則返回resolve;失敗則返回reject,並阻止圖片上傳
  checkImageWH(file) {
    let self = this;
    return new Promise(function(resolve, reject) {
      let filereader = new FileReader();
      filereader.onload = e => {
        let src = e.target.result;
        const image = new Image();
        image.onload = function() {
          // 獲取圖片的寬高,並存放到file對象中
          console.log('file width :' + this.width);
          console.log('file height :' + this.height);
          file.width = this.width;
          file.height = this.height;
          resolve();
        };
        image.onerror = reject;
        image.src = src;
      };
      filereader.readAsDataURL(file);
    });
  }

  handleSubmit = e => {
    const { dispatch, form } = this.props;
    e.preventDefault();
    form.validateFieldsAndScroll((err, values) => {// values 是form表單里的參數
      // 點擊按鈕後,將表單提交給後臺
      dispatch({
        type: 'mymodel/submitFormData',
        payload: values,
      });
    });
  };

  render() {
    const { previewVisible, previewImage, imgList } = this.state; //  從 state 中拿數據
    const uploadButton = (
      <div>
        <Icon type="plus" />
        <div className="ant-upload-text">Upload</div>
      </div>
    );
    return (
      <div className="clearfix">
        <Form onSubmit={this.handleSubmit} hideRequiredMark style={{ marginTop: 8 }}>
          <FormItem label="圖片圖片" {...formItemLayout}>
            {getFieldDecorator('myImg')(
              <Upload
                action="//jsonplaceholder.typicode.com/posts/" // 這個是介面請求
                data={file => ({ // data里存放的是介面的請求參數
                  param1: myParam1,
                  param2: myParam2,
                  photoCotent: file, // file 是當前正在上傳的圖片
                  photoWidth: file.height, // 通過  handleBeforeUpload 獲取 圖片的寬高
                  photoHeight: file.width,
                })}
                listType="picture-card"
                fileList={this.state.imgList}
                onPreview={this.handlePreview} // 點擊圖片縮略圖,進行預覽
                beforeUpload={this.handleBeforeUpload} // 上傳之前,對圖片的格式做校驗,並獲取圖片的寬高
                onChange={this.handleChange} // 每次上傳圖片時,都會觸發這個方法
              >
                {this.state.imgList.length >= 9 ? null : uploadButton}
              </Upload>
            )}
          </FormItem>
        </Form>
        <Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
          <img alt="example" style={{ width: '100%' }} src={previewImage} />
        </Modal>
      </div>
    );
  }
}

export default PicturesWall;

上傳後,點擊圖片預覽,瀏覽器卡死的問題

依據上方的代碼,通過 Antd 的 upload 組件將圖片上傳成功後,點擊圖片的縮略圖,理應可以在當前頁面彈出 Modal,預覽圖片。但實際的結果是,瀏覽器一定會卡死。

定位問題發現,原因竟然是:圖片上傳成功後, upload 會將其轉為 base64編碼。base64這個字元串太大了,點擊圖片預覽的時候,瀏覽器在解析一大串字元串,然後就卡死了。詳細過程描述如下。

上方代碼中,我們可以把 handleChange(file, fileList)方法中的 file、以及 fileList列印出來看看。 file指的是當前正在上傳的 單個 img,fileList是已上傳的全部 img 列表。 當我上傳完 兩張圖片後, 列印結果如下:

file的列印的結果如下:

    {
        "uid": "rc-upload-1551084269812-5",
        "width": 600,
        "height": 354,
        "lastModified": 1546701318000,
        "lastModifiedDate": "2019-01-05T15:15:18.000Z",
        "name": "e30e7b9680634b2c888c8bb513cc595d.jpg",
        "size": 31731,
        "type": "image/jpeg",
        "percent": 100,
        "originFileObj": {
            "uid": "rc-upload-1551084269812-5",
            "width": 600,
            "height": 354
        },
        "status": "done",
        "thumbUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAHQ9qKKlbimcXrIH9o2vH/AC2T+ddPj98v+9RRWsuhnHdk0ar9qb5R0Pb6VPB/qh9aKKiRr0Irnt/vUDr+NFFJCRqWxJik5Pb+dLJ938aKK06mYSdKKKKBH//Z",
        "response": {
            "retCode": 0,
            "imgUrl": "http://qianguyihao.com/opfewfwj098902kpkpkkj976fe.jpg",
            "photoid": 271850
        }
    }

fileList 的列印結果:

[
    {
        "uid": "rc-upload-1551084269812-3",
        "width": 1000,
        "height": 667,
        "lastModified": 1501414799000,
        "lastModifiedDate": "2017-07-30T11:39:59.000Z",
        "name": "29381f30e924b89914e91b33.jpg",
        "size": 135204,
        "type": "image/jpeg",
        "percent": 100,
        "originFileObj": {
            "uid": "rc-upload-1551084269812-3",
            "width": 1000,
            "height": 667
        },
        "status": "done",
        "thumbUrl": "data:image/jpeg;base64,/E3ju1tlaK1fzJOnHQU3LsLV7HO6Zrk11MZJ7luT0A4FZuRagi9quvzQQ4iuEJ7ZpqTG4djDsPFl2Lg733f8C4q+YhQ8zoYfGSqoMmfwo5huLL0HjiyPDSYPvxRdC1XQvxeLrB8fvl/OnoLmL9vrdvvYS3NGFVe2YsASOh71JfQyrqV2mXLHOcccVSIYEnDyZO9XXB9KYH//Z",
        "response": {
            "retCode": 0,
            "msg": "success",
            "imgUrl": "http://qianguyihao.com/hfwpjouiurewnmbhepr689.jpg",
        }
    },
    {
        "uid": "rc-upload-1551084269812-5",
        "width": 600,
        "height": 354,
        "lastModified": 1546701318000,
        "lastModifiedDate": "2019-01-05T15:15:18.000Z",
        "name": "e30e7b9680634b2c888c8bb513cc595d.jpg",
        "size": 31731,
        "type": "image/jpeg",
        "percent": 100,
        "originFileObj": {
            "uid": "rc-upload-1551084269812-5",
            "width": 600,
            "height": 354
        },
        "status": "done",
        "thumbUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAHQ9qKKlbimcXrIH9o2vH/AC2T+ddPj98v+9RRWsuhnHdk0ar9qb5R0Pb6VPB/qh9aKKiRr0Irnt/vUDr+NFFJCRqWxJik5Pb+dLJ938aKK06mYSdKKKKBH//Z",
        "response": {
            "retCode": 0,
            "imgUrl": "http://qianguyihao.com/opfewfwj098902kpkpkkj976fe.jpg",
            "photoid": 271850
        }
    }
]

上方的json數據中,需要做幾點解釋:

(1)response 欄位裡面的數據,就是請求介面後,後臺返回給前端的數據,裡面包含了圖片的url鏈接。

(2)status 欄位里存放的是圖片上傳的實時狀態,包括上傳中、上傳完成、上傳失敗。

(3)thumbUrl欄位裡面存放的是圖片的base64編碼。

這個base64編碼非常非常長。當點擊圖片預覽的時候,其實就是載入的 thumbUrl 這個欄位里的資源,難怪瀏覽器會卡死。

解決辦法:在 handleChange方法里,圖片上傳成功後,將 thumbUrl 欄位裡面的 base64 編碼改為真實的圖片url。代碼實現如下:

  handleChange = ({ file, fileList }) => {
    console.log(JSON.stringify(file)); // file 是當前正在上傳的 單個 img
    console.log(JSON.stringify(fileList)); // fileList 是已上傳的全部 img 列表


    // 【重要】將 圖片的base64替換為圖片的url。 這一行一定不會能少。
    // 圖片上傳成功後,fileList數組中的 thumbUrl 中保存的是圖片的base64字元串,這種情況,導致的問題是:圖片上傳成功後,點擊圖片縮略圖,瀏覽器會會卡死。而下麵這行代碼,可以解決該bug。
    fileList.forEach(imgItem => {
      if (imgItem && imgItem.status == 'done' && imgItem.response && imgItem.response.imgUrl) {
        imgItem.thumbUrl = imgItem.response.imgUrl;
      }
    });

    this.setState({
      imgList: fileList,
    });
  };

新需求:編輯現有頁面

上面一段的代碼中,我們是在新建的頁面中,從零開始上傳圖片。

現在有個新的需求:如何編輯現有的頁面呢?也就是說,現有的頁面在初始化時,是預設有幾張圖片的。當我編輯這個頁面時,可以對現有的圖片做增刪,也能增加新的圖片。而且要保證:新建頁面和編輯現有頁面,是共用一套代碼。

我看到upload 組件有提供 defaultFileList 的屬性。我試了下,這個defaultFileList 的屬性根本沒法兒用。

那就只要手動實現了。我的model層代碼,是用 redux 寫的。整體的實現思路如下:(這個也是在真正在實戰中用到的代碼)

(1)PicturesWall.js:

/* eslint-disable */

import { Upload, Icon, Modal, Form } from 'antd';

const FormItem = Form.Item;

class PicturesWall extends PureComponent {
  state = {
    previewVisible: false,
    previewImage: '',
  };

  // 頁面初始化的時候,從介面拉取預設的圖片數據
  componentDidMount() {
    const { dispatch } = this.props;
    dispatch({
      type: 'mymodel/getAllInfo',
      payload: { params: xxx },
    });
  }

  handleChange = ({ file, fileList }) => {
    const { dispatch } = this.props;
    // 【重要】將 圖片的base64替換為圖片的url。 這一行一定不會能少。
    // 圖片上傳成功後,fileList數組中的 thumbUrl 中保存的是圖片的base64字元串,這種情況,導致的問題是:圖片上傳成功後,點擊圖片縮略圖,瀏覽器會會卡死。而下麵這行代碼,可以解決該bug。
    fileList.forEach(imgItem => {
      if (imgItem && imgItem.status == 'done' && imgItem.response && imgItem.response.imgUrl) {
        imgItem.thumbUrl = imgItem.response.imgUrl;
      }
    });

    dispatch({
      type: 'mymodel/setImgList',
      payload: fileList,
    });
  };

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

  handlePreview = file => {
    this.setState({
      previewImage: file.url || file.thumbUrl,
      previewVisible: true,
    });
  };

  // 參考鏈接:https://www.jianshu.com/p/f356f050b3c9
  handleBeforeUpload = file => {
    //限製圖片 格式、size、解析度
    const isJPG = file.type === 'image/jpeg';
    const isJPEG = file.type === 'image/jpeg';
    const isGIF = file.type === 'image/gif';
    const isPNG = file.type === 'image/png';
    const isLt2M = file.size / 1024 / 1024 < 2;


    if (!(isJPG || isJPEG || isGIF || isPNG)) {
      Modal.error({
        title: '只能上傳JPG 、JPEG 、GIF、 PNG格式的圖片~',
      });
    } else if (!isLt2M) {
      Modal.error({
        title: '超過2M限制 不允許上傳~',
      });
    }

  }

    // 參考鏈接:https://github.com/ant-design/ant-design/issues/8779
    return new Promise((resolve, reject) => {
      if (!(isJPG || isJPEG || isGIF || isPNG)) {
        reject(file);
      } else {
        resolve(file && this.checkImageWH(file));
      }
    });

  };

  //返回一個 promise:檢測通過則返回resolve;失敗則返回reject,並阻止圖片上傳
  checkImageWH(file) {
    let self = this;
    return new Promise(function(resolve, reject) {
      let filereader = new FileReader();
      filereader.onload = e => {
        let src = e.target.result;
        const image = new Image();
        image.onload = function() {
          // 獲取圖片的寬高,並存放到file對象中
          console.log('file width :' + this.width);
          console.log('file height :' + this.height);
          file.width = this.width;
          file.height = this.height;
          resolve();
        };
        image.onerror = reject;
        image.src = src;
      };
      filereader.readAsDataURL(file);
    });
  }

  handleSubmit = e => {
    const { dispatch, form } = this.props;
    e.preventDefault();

    const {
      mymodel: { imgList }, // 從props中拿預設的圖片數據
    } = this.props;

    form.validateFieldsAndScroll((err, values) => {
      // values 是form表單里的參數
      // 點擊按鈕後,將表單提交給後臺


      // start 問題描述:當編輯現有頁面時,如果針對已經存在的預設圖片不做修改,則不會觸發 upload 的 onChange方法。此時提交表單,表單里的 myImg 欄位是空的。
      // 解決辦法:如果發現存在預設圖片,則追加到表單中
      if (!values.myImg) {

        values.myImg = { fileList: [] };

        values.myImg.fileList = imgList;
      }
      // end

      dispatch({
        type: 'mymodel/submitFormData',
        payload: values,
      });
    });
  };

  render() {
    const { previewVisible, previewImage } = this.state; //  從 state 中拿數據

    const {
      mymodel: { imgList }, // 從props中拿到的圖片數據
    } = this.props;

    const uploadButton = (
      <div>
        <Icon type="plus" />
        <div className="ant-upload-text">Upload</div>
      </div>
    );
    return (
      <div className="clearfix">
        <Form onSubmit={this.handleSubmit} hideRequiredMark style={{ marginTop: 8 }}>
          <FormItem label="圖片上傳" {...formItemLayout}>
            {getFieldDecorator('myImg')(
              <Upload
                action="//jsonplaceholder.typicode.com/posts/" // 這個是介面請求
                data={file => ({
                  // data里存放的是介面的請求參數
                  param1: myParam1,
                  param2: myParam2,
                  photoCotent: file, // file 是當前正在上傳的圖片
                  photoWidth: file.height, // 通過  handleBeforeUpload 獲取 圖片的寬高
                  photoHeight: file.width,
                })}
                listType="picture-card"
                fileList={imgList}  // 改為從 props 里拿圖片數據,而不是從 state
                onPreview={this.handlePreview} // 點擊圖片縮略圖,進行預覽
                beforeUpload={this.handleBeforeUpload} // 上傳之前,對圖片的格式做校驗,並獲取圖片的寬高
                onChange={this.handleChange} // 每次上傳圖片時,都會觸發這個方法
              >
                {this.state.imgList.length >= 9 ? null : uploadButton}
              </Upload>
            )}
          </FormItem>
        </Form>
        <Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
          <img alt="example" style={{ width: '100%' }} src={previewImage} />
        </Modal>
      </div>
    );
  }
}

export default PicturesWall;

(2)mymodel.js:

/* eslint-disable */

import { routerRedux } from 'dva/router';
import { message, Modal } from 'antd';
import {
  getGoodsInfo,
  getAllGoods,
} from '../services/api';
import { trim, getCookie } from '../utils/utils';

export default {
  namespace: 'mymodel',

  state: {
    form: {},
    list: [],
    listDetail: [],
    goodsList: [],
    goodsListDetail: [],
    pagination: {
      pageSize: 10,
      total: 0,
      current: 1,
    },
    imgList: [], //圖片
  },
  subscriptions: {
    setup({ dispatch, history }) {
      history.listen(location => {
        if (location.pathname !== '/xx/xxx') return;
        if (!location.state || !location.state.xxxId) return;
        dispatch({
          type: 'fetch',
          payload: location.state,
        });
      });
    },
  },

  effects: {
    // 介面。獲取所有工廠店的列表 (步驟02)
    *getAllInfo({ payload }, { select, call, put }) {
      yield put({
        type: 'form',
        payload,
      });
      console.log('params:' + JSON.stringify(payload));

      let params = {};
      params = payload;

      const response = yield call(getGoodsInfo, params);

      console.log('smyhvae response:' + JSON.stringify(response));
      if (response.error) return;
      yield put({
        type: 'allInfo',
        payload:
          (response.data &&
            response.data.map(item => ({
              xx1: item.yy1,
              xx2: item.yy2,
            }))) ||
          [],
      });

      // response 里包含了介面返回給前端的預設圖片數據
      if (response && response.data && response.data[0] && response.data[0].my_jpg) {
        let tempImgList = response.data[0].my_jpg.split(',');
        let imgList = [];

        if (tempImgList.length > 0) {
          tempImgList.forEach(item => {
            imgList.push({
              uid: item,
              name: 'xxx.png',
              status: 'done',
              thumbUrl: item,
            });
          });
        }

        // 通過  redux的方式 將 預設圖片 傳給 imgList
        console.log('smyhvae payload imgList:' + JSON.stringify(imgList));
        yield put({
          type: 'setImgList',
          payload: imgList,
        });
      }
    },

    *setImgList({ payload }, { call, put }) {
      console.log('model setImgList');
      yield put({
        type: 'getImgList',
        payload,
      });
    },
  },

  reducers: {
    allInfo(state, action) {
      return {
        ...state,
        list: action.payload,
      };
    },
    getImgList(state, action) {
      return {
        ...state,
        imgList: action.payload,
      };
    },
  },
};

上面的代碼,可以規避 upload 組件的一些bug;而且可以在上傳前,通過校驗圖片的尺寸、大小等,如果不滿足條件,則彈出modal彈窗,阻止上傳。

大功告成。本文感謝 ld 同學的支持。

其他問題

最後一段

有人說,前端開發,連賣菜的都會。可如果真的遇到技術難題,還是得找個靠譜的前端同學才行。這不,來看看前端碼農日常:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android遠程桌面助手(B1413),優化性能,提高相容性和連接的可靠性,解決Android 9上顯示黑屏問題。 ...
  • 基本思路 聖杯佈局分為3段:上、中、下。 中段被分為:左、中、右3塊。 1:採用flex佈局時,先把彈性容器主軸設置為垂直方向(flex-direction:column) 2:上、中、下3塊彈性項目設置均勻拉伸(flex:1)或固定上、下兩端大小,讓中間自動拉伸。註意:flex:拉伸是方向為主軸方 ...
  • 1:創建一個彈性容器(display:flex) 2:構建2個或3個彈性項目. 3:把彈性項目設置為居中對齊.(align-items:center) 4:改變input自身對齊方式,把它設置為拉伸以適應容器(align-self:stretch)。 實例: ...
  • 解決問題:大同大學教務處官網教師埠一進去就卡住了,點上面一行的菜單無響應 教師端下載方法: 鏈接:https://pan.baidu.com/s/10E-_0C7xVcKvVIyWxWHEVw 提取碼:e1ik 學生端下載方法: 鏈接:https://pan.baidu.com/s/1MMmAVd ...
  • 繼承 記錄一下 javascript 的各種繼承方式,個人用得比較多的還是原型鏈繼承和 ES6 的 extends。 原型鏈繼承 缺點: 在創建 Child 的實例時,無法向 Parents 傳參 父類裡面的引用類型被共用,個例修改導致所有實例都被修改 借用構造函數 為瞭解決上面的問題 ,經典繼承方 ...
  • 作為一枚前段,我們知道對象類型在賦值的過程中其實是複製了地址,從而會導致改變了一方其他也都被改變的情況。通常在開發中我們不希望出現這樣的問題,我們可以使用淺拷貝來解決這個情況。 淺拷貝 首先可以通過Object.assign來解決這個問題,很多人認為這個函數是用來深拷貝的。其實並不是,Object. ...
  • 背景 經測試,android手機中沒有這個問題, iphone手機中的Safari瀏覽器會出現這個問題。 例如: 解決辦法: 給鏈接加上 target="_parent", 如果iframe的嵌套比較深可以用 target="_top" ...
  • 有時直接打開本地html文件會使一些web操作無法進行,需要運行一個本地伺服器。 使用nodejs的 可以迅速地啟動一個本地靜態資源伺服器 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...