微信小程序上传图片组件,图片实现拖拽排序

  • 微信小程序上封装上传图片组件

  • 安装 weapp

  • components

  • components/imgUp/index.wxml

      <view class="img-box">
        <HFilesImg fileList="{{fileList}}"  show-upload="{{showUpload}}" multiple="{{multiple}}"  bind:updateImageList="updateImageList"  maxCount="{{maxCount}}"  previewSize="{{previewSize}}"   deletable="{{deletable}}"    bind:onAfterRead="onAfterRead" bind:onDelete="onDelete"></HFilesImg>
      </view>
    
  • components/imgUp/index.json

      {
        "component": true,
        "usingComponents": {
          "van-cell": "@vant/weapp/cell/index",
          "HFilesImg": "../h-files-img/index"
        }
      }
    
  • components/imgUp/index.ts

      import {  http } from "../../utils/api";
    
      Component({
        /**
        * 组件的属性列表
        */
        properties: {
          value: {
            type: Array,
            value: [],
          },
          type: {
            type: String,
            value: "",
          },
          Id: {
            type: String,
            value: "",
          },
          position: {
            type: String,
            value: "",
          },
          multiple: {
            type: Boolean,
            value: true,
          },
          deletable: {
            type: Boolean,
            value: true,
          },
          showUpload: {
            type: Boolean,
            value: true,
          },
          maxCount: {
            type: Number,
            value: 9,
          },
          uploadUrl: {
            type: String,
            value: "修改图片url地址",
          },
          deleteUrl: {
            type: String,
            value: "删除图片url地址",
          },
          gap: { type: Number, value: 9 },
          columns: { type: Number, value: 3 }, //  // 列数
          previewSize: { type: Number, value: 100 }, // 图片大小
          deleteStyle: { type: String, value: "" },
        },
    
        /**
        * 组件的初始数据
        */
        data: {
          fileList: [],
        },
        lifetimes: {
          attached: function () {
          },
        },
    
        /**
        * 组件的方法列表
        */
        methods: {
          updateImageList(e: any) {
            console.log(e.detail);
            if (e.detail.length === 0) {
              return;
            } else {
              const list = e.detail.map((el: any, index: number) => ({
                id: el.id,
                order: index + 1,
              }));
              http({
                url: "NewHousing/MoveChange",
                method: "POST",
                data: {
                  movechangelist: list,
                },
              }).then((res: any) => {
                if (res.errCode === 0) {
                  console.log(res);
                }
              });
            }
          },
          // 文件读取完成
          onAfterRead(e: any) {
            e.detail.file.map((s: any) => {
              if (s.size > 4 * 1024 * 1024) {
                wx.showModal({
                  content: "请上传小于4M的图片",
                  showCancel: false,
                  success: () => {},
                });
              } else {
                this.data.fileList.push({
                  url: s.url,
                  name: "图片" + this.data.fileList.length,
                  state: 0, // 0:上传中  1:上传成功 2:上传失败
                  fileId: null,
                });
              }
            });
            this.setData({
              fileList: this.data.fileList,
            });
            this.onAddFiles();
          },
          /** 附件上传 */
          onAddFiles() {
            let formDataObj: any = {};
            if (this.properties.Id) {
              formDataObj.id = this.properties.Id;
            }
    
            for (let index = 0; index < this.data.fileList.length; index++) {
              const el:any = this.data.fileList[index];
              wx.uploadFile({
                url: this.properties.Id ? this.properties.uploadUrl : '新增url地址',
                filePath: el.url,
                name: "file", //上传的所需字段,后端提供
                header: {
                  Authorization: 'token值',
                },
                formData: formDataObj,
                success: (res: any) => {
                  const data = JSON.parse(res.data);
                  const arr: any[] = this.data.fileList.concat([]);
                  arr[index]["id"] = data.data.id;
                  arr[index]["url"] = data.data.filepath;
                  arr[index]["state"] = 1;
                  this.setData({ fileList: arr });
                  this.triggerEvent("onChange", this.data.fileList);
                },
                fail: () => {
                  const arr: any[] = this.data.fileList.concat([]);
                  arr[index]["state"] = 2;
                  this.setData({ fileList: arr });
                  this.triggerEvent("onChange", this.data.fileList);
                },
              });
            }
          },
    
          // 点击删除文件
          onDelete(e: any) {
            if (this.properties.Id) {
              const that = this;
              wx.showModal({
                title: "提示",
                content: "确定要删除图片吗?",
                success(res) {
                  if (res.confirm) {
                    console.log("用户点击确定");
                    const Id: any = that.properties.Id;
                    const list: any = {
                      id: Number(Id),
                      attachid: e.detail.file.id, // 附件id
                    };
                    if (that.properties.type || that.properties.type === "0") {
                      list.type = that.properties.type;
                    }
                    that.getDelete(list,that.properties.deleteUrl, e.detail.index);
                  }
                },
              });
            } else {
              this.data.fileList.splice(e.detail.index, 1);
              this.setData({
                fileList: this.data.fileList,
              });
              this.triggerEvent("onChange", this.data.fileList);
            }
          },
          getDelete(list: any, url: any, index: any) {
            wx.showLoading({
              title: "loading...",
            });
            http({
              url: url,
              method: "POST",
              data: list,
            })
              .then((res: any) => {
                if (res.errCode === 0) {
                  wx.showToast({
                    title: "删除成功",
                    icon: "success",
                  });
                  this.data.fileList.splice(index, 1);
                  this.setData({
                    fileList: this.data.fileList,
                  });
                  this.triggerEvent("onChange", this.data.fileList);
                }
              })
              .catch((err: any) => {
                const data = err.data;
                wx.showModal({
                  title: "提示",
                  showCancel: false,
                  content: `${data.Message}`,
                });
              })
              .then(() => {
                wx.hideLoading();
              });
          },
        },
        observers: {
          value: function (value) {
            this.setData({
              fileList: value || [],
            });
          },
        },
      });
    
  • components/imgUp/index.scss

      .img-box {
        padding: 0 13px 13px;
        background-color: #fff;
      }
    
  • h-files-img 拖拽图片上传组件
  • components/h-files-img/index.wxml

      <view style="width: {{containerRes.width}}px; height: {{containerRes.height}}px;" class="drag-container">
        <view wx:for="{{dragImgList}}" wx:key="index" catch:tap="openImg"  data-url="{{item.url}}"  style="transform: translate({{index === currentIndex ? tranX : item.tranX}}px, {{index === currentIndex ? tranY : item.tranY}}px); z-index: {{index === currentIndex ? 10 : 1}}; width: {{previewSize}}px; height: {{previewSize}}px;" class="drag-item drag-item-transition" mark:index="{{index}}" mark:key="{{item.key}}"  catch:longpress="longPress"  catch:touchmove="touchMove" catch:touchend="touchEnd">
          <image class="drag-item-img" src="{{item.url}}" />
          <view   wx:if="{{deletable}}"  catch:tap="deleteImg" mark:key="{{index}}" data-item="{{item}}" class="drag-item-delete">
            <view class="drag-item-delete_default" style="{{deleteStyle}}">x</view>
          </view>
        </view>
    
        <view wx:if="{{showUpload}}">
          <view catch:tap="uploadImage" class="drag-item drag-upload" hidden="{{dragImgList.length >= maxCount}}" style="transform: translate({{uploadPosition.tranX}}px, {{uploadPosition.tranY}}px); width: {{previewSize}}px; height: {{previewSize}}px;">
            <view class="drag-upload_solt">
              <slot name="upload"></slot>
            </view>
            <view class="drag-upload_default">
              <van-icon name="plus" color="#dcdee0"  size="24px"  />
            </view>
          </view>
        </view>
      </view>
    
  • components/h-files-img/index.json

      {
        "component": true,
        "usingComponents": {
          "van-uploader": "@vant/weapp/uploader/index",
          "van-icon": "@vant/weapp/icon/index"
        }
      }
    
  • components/h-files-img/index.ts

      Component({
        options: { multipleSlots: !0 },
        properties: {
          showUpload: {
            type: Boolean,
            value: false,
          },
          previewSize: { type: Number, value: 100 }, // 图片大小
          fileList: { // 初始化图片数组
            type: Array,
            value: [],
            observer(t) {
              if (t?.length) {
                const e = this.getDragImgList(t);
                this.setUploaPosition(e.length),
                this.setData({ dragImgList: e })
              } else {
                this.setData({
                  dragImgList: [],
                  uploadPosition: {
                    tranX: 0,
                    tranY: 0,
                  }
                })
              }
            }
          },
          deletable: {
            type: Boolean,
            value: false,
          },
          maxCount: { type: Number, value: 9 },// 图片上传数量限制
          columns: { type: Number, value: 3 }, //  // 列数
          gap: { type: Number, value: 15 }, // // 图片间隔
          deleteStyle: { type: String, value: "" } // 删除样式
        },
        data: {
          dragImgList: [],
          containerRes: {
            top: 0, // 容器距离页面顶部距离 px
            left: 0, // 容器距离页面左边距离 px
            width: 100, // 容器宽度 px
            height: 100, // 容器高度 px
          }, // 拖拽容器属性
          currentKey: -1, // 正在拖拽图片的key
          currentIndex: -1, // 正在拖拽图片的index
          tranX: 0, // 正在拖拽图片移动的x距离
          tranY: 0, // 正在拖拽图片移动的y距离
          uploadPosition: { // upload上传图标位移距离
            tranX: 0,
            tranY: 0,
          },
          type: ''
        },
        lifetimes: {
          ready() {
            this.createSelectorQuery().select(".drag-container").boundingClientRect((({
              top: t, left: e }) => {
              this.setData({ "containerRes.top": t, "containerRes.left": e })
            })).exec()
          }
        },
        methods: {
          openImg(e: any) {
            const array = this.data.dragImgList.map((el:any) => el.url)
            wx.previewImage({
              current: e.currentTarget.dataset.url, // 当前显示图片的http链接
              urls: array // 需要预览的图片http链接列表
            })
          },
          /**
          * 长按图片
          */
          longPress(e: any) {
            const index = e.mark.index;
            const { pageX, pageY } = e.touches[0];
            const { previewSize, containerRes: { top, left } } = this.data;
            this.setData({
              currentIndex: index,
              tranX: pageX - previewSize / 2 - left,
              tranY: pageY - previewSize / 2 - top,
              type: 'move'
            });
          },
          /**
          * touchMove
          */
          touchMove(e: any) {
            // 如果currentIndex < 0,说明并没有触发longPress
            if (this.data.currentIndex < 0) {
              return;
            }
            const { pageX, pageY } = e.touches[0];
            const { previewSize, containerRes: { top, left } } = this.data;
            const tranX = pageX - previewSize / 2 - left;
            const tranY = pageY - previewSize / 2 - top;
            this.setData({
              tranX,
              tranY,
              type: 'move'
            });
            // 对比当前移动的key和停放位置的key,如果不一样就修改位置
            const currentKey = e.mark.key;
            const moveKey = this.getMoveKey(tranX, tranY);
    
            // 当移动的key和正在停放位置的key相等,就无须处理
            if (currentKey === moveKey || this.data.currentKey === currentKey) {
              return;
            }
            this.data.currentKey = currentKey;
            this.replace(currentKey, moveKey);
          },
          /**
          * 计算移动中的key
          * @param tranX 正在拖拽图片的tranX
          * @param tranY 正在拖拽图片的tranY
          */
          getMoveKey(tranX: number, tranY: number) {
            const { dragImgList: list, previewSize, columns } = this.data;
            const _getPositionNumber = (drag: any, limit: number) => {
              const positionNumber = Math.round(drag / previewSize);
              return positionNumber >= limit ? limit - 1 : positionNumber < 0 ? 0 : positionNumber;
            }
            const endKey = columns * _getPositionNumber(tranY, Math.ceil(list.length / columns)) + _getPositionNumber(tranX, columns);
            return endKey >= list.length ? list.length - 1 : endKey;
          },
          /**
          * 生成拖拽后的新数组
          * @param start 拖拽起始的key
          * @param end 拖拽结束的key
          */
          replace(start: number, end: number) {
            const dragImgList = this.data.dragImgList;
            dragImgList.forEach((item: any) => {
              if (start < end) {
                if (item.key > start && item.key <= end) item.key--;
                else if (item.key === start) item.key = end;
              } else if (start > end) {
                if (item.key >= end && item.key < start) item.key++;
                else if (item.key === start) item.key = end;
              }
            });
            this.getListPosition(dragImgList);
          },
          /**
          * 计算数组的位移位置
          * @param list 拖拽图片数组
          */
          getListPosition(list: any) {
            const { previewSize, columns, gap } = this.data;
            const dragImgList = list.map((item: any) => {
              item.tranX = (previewSize + gap) * (item.key % columns);
              item.tranY = Math.floor(item.key / columns) * (previewSize + gap);
              return item;
            })
            this.setData({
              dragImgList,
            });
            this.updateEvent(dragImgList);
          },
          /**
          * touchEnd
          */
          touchEnd() {
            this.setData({
              tranX: 0,
              tranY: 0,
              currentIndex: -1,
              type: 'move'
            });
            this.data.currentKey = -1;
          },
          /**
          * updateEvent
          * @describe 上传删除拖拽后触发事件把列表数据发给页面
          */
          updateEvent(dragImgList: any) {
            const list = [...dragImgList].sort((a, b) => a.key - b.key).map((item) => ({
              url: item.url,
              id: item.id
            }));
            this.triggerEvent('updateImageList', list);
          },
          /**
      * 上传图片
      */
          async uploadImage() {
            let { dragImgList, maxCount } = this.data;
            try {
              const res = await wx.chooseMedia({
                count: maxCount - dragImgList.length,
                mediaType: ['image'],
              });
              // 获取上传图片数据后需要初始化图片拽结构
              const imgList = this.getDragImgList(res?.tempFiles?.map(({ tempFilePath, size }) => ({ tempFilePath, size })) || [], false);
              dragImgList = dragImgList.concat(imgList);
              // 修改上传区域位置
              this.setUploaPosition(dragImgList.length);
              this.setData({
                dragImgList,
                type: ''
              });
              const list = {
                file: imgList
              }
              this.triggerEvent("onAfterRead", list);
            } catch (error) {
              console.log(error);
            }
          },
          /**
          * 改变图片数量后获取容器宽高
          * @parma listLength 数组长度
          */
          getContainerRect(listLength: number) {
            const { columns, previewSize, maxCount, gap } = this.data;
            const number = listLength === maxCount ? listLength : listLength + 1;
            const row = Math.ceil(number / columns)
            return {
              width: columns * previewSize + (columns - 1) * gap,
              height: row * previewSize + gap * (row - 1),
            };
          },
          /**
          * 根据图片列表生成拖拽列表数据结构
          * @param list 图片url列表
          * @param init 是否是初始化
          */
          getDragImgList(list: any, init = true) {
            let { dragImgList, previewSize, columns, gap } = this.data;
            return list.map((item: any, index: number) => {
              const i = (init ? 0 : dragImgList.length) + index;
              return {
                tranX: (previewSize + gap) * (i % columns),
                tranY: Math.floor(i / columns) * (previewSize + gap),
                url: item.tempFilePath ? item.tempFilePath : item.url,
                size: item.size || '',
                id: item.id ? item.id : null,
                key: i,
              };
            });
          },
          /**
          * 修改上传区域位置
          * @param listLength 数组长度
          */
          setUploaPosition(listLength: number) {
            const { previewSize, columns, gap } = this.data;
            const uploadPosition = {
              tranX: listLength % columns * (previewSize + gap),
              tranY: Math.floor(listLength / columns) * (previewSize + gap),
            };
            const { width, height } = this.getContainerRect(listLength);
            this.setData({
              uploadPosition,
              ['containerRes.width']: width,
              ['containerRes.height']: height,
            });
          },
          /**
          * 删除图片
          */
          deleteImg(e: any) {
            const key = e.mark.key;
            const obj = {
              file: e.currentTarget.dataset.item,
              index: key
            }
            this.triggerEvent("onDelete", obj);
    
          },
    
        }
      });
    
  • components/h-files-img/index.scss

      .drag-container {
        position: relative;
      }
      .drag-item {
        position: absolute;
        top: 0;
        left: 0;
      }
      .drag-item-transition {
        transition: transform 0.1s
      }
      .drag-item-img {
        width: 100%;
        height: 100%;
      }
      .drag-item-delete {
        position: absolute;
        top: 0;
        right: 0;
        touch-action: none;
      }
      .drag-item-delete_default {
        display: flex;
        width: 21px;
        height: 15px;
        line-height: 10px;
        justify-content: center;
        background-color: rgba(0, 0, 0, 0.7);
        border-radius: 0 0 0 12px;
        color: #FEFEFE;
      }
      .drag-upload .drag-upload_solt:empty+.drag-upload_default {
        width: 100%;
        height: 100%;
        box-sizing: border-box;
        text-align: center;
        font-weight: bold;
        line-height: 100%;
        background-color: #f7f8fa;
      }
      .drag-upload .drag-upload_default {
        display: flex;
        align-items: center;
        justify-content: center;
    
      }
    
  • h-files-img 纯图片上传
  • components/h-files-img/index.wxml

      <view style="max-width:{{width}}px;">
        <van-uploader file-list="{{ fileList }}" preview-size="{{previewSize}}" multiple="{{multiple}}" upload-icon="plus" show-upload="{{showUpload}}" deletable="{{deletable}}" max-count="{{maxCount}}" bind:delete="onDelete" bind:after-read="afterRead" />
      </view>
    
  • components/h-files-img/index.json

      {
        "component": true,
        "usingComponents": { "van-uploader": "@vant/weapp/uploader/index" }
      }
    
  • components/h-files-img/index.ts

      Component({
        /**
        * 组件的属性列表
        */
        properties: {
          name: {
            type: String,
            value: "img",
          },
          fileList: {
            type: Array,
            value: [],
          },
          previewSize: {
            type: Number,
            value: 80,
          },
          multiple: {
            type: Boolean,
            value: true,
          },
          showUpload: {
            type: Boolean,
            value: false,
          },
          deletable: {
            type: Boolean,
            value: false,
          },
          maxCount: {
            type: Number,
            value: 9,
          },
          columnNum: {
            type: Number,
            value: 4,
          },
        },
    
        /**
        * 组件的初始数据
        */
        data: {
          width: 352,
        },
    
        lifetimes: {
          attached() {
            this.setData({
              width: (this.data.previewSize + 8) * this.data.columnNum,
            });
          },
        },
    
        /**
        * 组件的方法列表
        */
        methods: {
          // 文件读取完成后
          afterRead(event) {
            this.triggerEvent("onAfterRead", event.detail);
          },
    
          // 点击删除文件
          onDelete(e) {
            this.triggerEvent("onDelete", e.detail);
          },
        },
      });
    
  • pages

  • pages/index/index.wxml

      <imgUp maxCount="999"  Id="{{id}}"   value="{{imgList}}"  />
    
  • pages/index/index.json

      {
        "navigationBarTitleText": "拖拽图片",
        "usingComponents": {
          "imgUp": "/components/imgUp/index"
        }
      }
    
  • pages/index/index.ts

      data: {
        imgList: []
      }
    
  • utils/api.ts

      import { getToken } from "./util";
    
      const baseUrl =   "xx/api/"; // 后端接口
      export default baseUrl;
      export function http({ url = "", data = {}, method = "GET", header = {} }: IHttpOption = {}) {
        return new Promise(async (resolve: any, reject: any) => {
          const token = await getToken();
          hrefUrl =  getBaseUrl(url)
          wx.request({
            url: hrefUrl,
            data: data,
            header: {
              Authorization: token ? `${token.type} ${token.token}` : "",
              ...header,
            },
            method: method,
            complete: (res: any) => {
              if (res.statusCode >= 200 && res.statusCode < 300) {
                resolve(res.data);
              } else {
                const statusCode = res.statusCode;
                if (statusCode === 401) {
                  wx.clearStorage();
                  wx.showModal({
                    title: "提示",
                    content: "登录失效请重新登录",
                    showCancel: false,
                    success: function (res) {
                      if (res.confirm) {
                        wx.nextTick(() => {
                          wx.navigateTo({
                            url: "/pages/login/index",
                          });
                        });
                      }
                    },
                  });
                } else if (statusCode === 404) {
                    wx.showModal({
                      title: "提示",
                      content:"接口未发布请联系管理员",
                      showCancel: false,
                    });
                } else {
                  reject(res);
                }
              }
            },
          });
        });
      }
      export function httpVisitors({ url = "", data = {}, method = "GET", header = {} }: IHttpOption = {}) {
        return new Promise(async (resolve: any, reject: any) => {
          wx.request({
            url: getBaseUrl(url),
            data: data,
            header: {
              ...header,
            },
            method: method,
            complete: (res: any) => {
              if (res.statusCode >= 200 && res.statusCode < 300) {
                resolve(res.data);
              } else {
                reject(res);
              }
            },
          });
        });
      }
      export function getBaseUrl(url: any) {
        if (url.indexOf("://") == -1) {
          url = baseUrl + url;
        }
        return url;
      }
      // get方法
      export function _get(url: any, data = {}) {
        return http({
          url,
          data,
        });
      }
      export function _post(url: any, data = {}) {
        return http({
          url,
          data,
          method: "POST",
        });
      }
    
  • 上传FormData格式

     import { httpVisitors } from "./api";
     const FormData = require('./formData.js')
     const formData = new FormData();
     formData.append('xxx', xxx);
     let data = formData.getData();
     httpVisitors({
          url: "",
          method: "POST",
          header: {
            'content-type': data.contentType,
          },
          data: data.buffer,
        })
          .then((res: any) => {})
    
  • utils/util.ts

      const FormData = require('./formData.js')
      /** 获取用户token */
      export async function getToken(username = '', password = '') {
        let token: any = _token;
        if (!token) {
          const tokenStr = wx.getStorageSync("token");
          if (tokenStr) {
            token = JSON.parse(tokenStr);
          }
        }
        if (token && Number(new Date()) < token.expires) {
          _token = token;
          return token;
        } else {
          wx.removeStorageSync("token");
        }
        const formData = new FormData();
        const name = username || wx.getStorageSync("loginName");
        const pass = password || wx.getStorageSync("loginPass");
        formData.append('username', name);
        formData.append('password', pass);
        formData.append('code', 2);
        let data = formData.getData();
        return await httpVisitors({
          url: "",
          method: "POST",
          header: {
            'content-type': data.contentType,
          },
          data: data.buffer,
        })
          .then((res: any) => {
            wx.hideLoading();
            if (res.errCode === -1) {
              wx.showModal({
                title: '提示',
                content: res.errMsg,
                showCancel: false,
              });
              return ''
            } else if (res.errCode === 0) {
              const data = res.data;
              const tokenJson = {
                type: 'bearer',
                token: data,
                expires: Number(new Date()) + 7200 * 1000,
              };
              _token = tokenJson;
              const tokenJsonStr = JSON.stringify(tokenJson);
              wx.setStorageSync("token", tokenJsonStr);
              return tokenJson;
            }
          })
          .catch(err => {
            const { statusCode } = err;
            if (statusCode === 404) {
              wx.hideLoading();
              wx.showModal({
                title: '提示',
                content: "接口未发布请联系管理员",
                showCancel: false,
              });
            } else {
              wx.hideLoading();
              return err;
            }
          });
    
      }
    
  • utils/formData.js

      const mimeMap = require('./mimeMap.js')
    
      function FormData(){
        let fileManager = wx.getFileSystemManager();
        let data = {};
        let files = [];
    
        this.append = (name, value)=>{
          data[name] = value;
          return true;
        }
    
        this.appendFile = (name, path, fileName)=>{
          let buffer = fileManager.readFileSync(path);
          if(Object.prototype.toString.call(buffer).indexOf("ArrayBuffer") < 0){
            return false;
          }
    
          if(!fileName){
            fileName = getFileNameFromPath(path);
          }
    
          files.push({
            name: name,
            buffer: buffer,
            fileName: fileName
          });
          return true;
        }
    
        this.getData = ()=>convert(data, files)
      }
    
      function getFileNameFromPath(path){
        let idx=path.lastIndexOf("/");
        return path.substr(idx+1);
      }
    
      function convert(data, files){
        let boundaryKey = 'wxmpFormBoundary' + randString(); // 数据分割符,一般是随机的字符串
        let boundary = '--' + boundaryKey;
        let endBoundary = boundary + '--';
    
        let postArray = [];
        //拼接参数
        if(data && Object.prototype.toString.call(data) == "[object Object]"){
          for(let key in data){
            postArray = postArray.concat(formDataArray(boundary, key, data[key]));
          }
        }
        //拼接文件
        if(files && Object.prototype.toString.call(files) == "[object Array]"){
          for(let i in files){
            let file = files[i];
            postArray = postArray.concat(formDataArray(boundary, file.name, file.buffer, file.fileName));
          }
        }
        //结尾
        let endBoundaryArray = [];
        endBoundaryArray.push(...endBoundary.toUtf8Bytes());
        postArray = postArray.concat(endBoundaryArray);
        return {
          contentType: 'multipart/form-data; boundary=' + boundaryKey,
          buffer: new Uint8Array(postArray).buffer
        }
      }
    
      function randString() {
        let res = "";
        for (let i = 0; i < 17; i++) {
          let n = parseInt(Math.random() * 62);
          if (n <= 9) {
            res += n;
          }
          else if (n <= 35) {
            res += String.fromCharCode(n + 55);
          }
          else {
            res += String.fromCharCode(n + 61);
          }
        }
        return res;
      }
    
      function formDataArray(boundary, name, value, fileName){
        let dataString = '';
        let isFile = !!fileName;
    
        dataString += boundary + '\r\n';
        dataString += 'Content-Disposition: form-data; name="' + name + '"';
        if (isFile){
          dataString += '; filename="' + fileName + '"' + '\r\n';
          dataString += 'Content-Type: ' + getFileMime(fileName) + '\r\n\r\n';
        }
        else{
          dataString += '\r\n\r\n';
          dataString += value;
        }
    
        var dataArray = [];
        dataArray.push(...dataString.toUtf8Bytes());
    
        if (isFile) {
          let fileArray = new Uint8Array(value);
          dataArray = dataArray.concat(Array.prototype.slice.call(fileArray));
        }
        dataArray.push(..."\r".toUtf8Bytes());
        dataArray.push(..."\n".toUtf8Bytes());
    
        return dataArray;
      }
    
      function getFileMime(fileName){
        let idx = fileName.lastIndexOf(".");
        let mime = mimeMap[fileName.substr(idx)];
        return mime?mime:"application/octet-stream"
      }
    
      String.prototype.toUtf8Bytes = function(){
        var str = this;
        var bytes = [];
        for (var i = 0; i < str.length; i++) {
          bytes.push(...str.utf8CodeAt(i));
          if (str.codePointAt(i) > 0xffff) {
            i++;
          }
        }
        return bytes;
      }
    
      String.prototype.utf8CodeAt = function(i) {
        var str = this;
        var out = [], p = 0;
        var c = str.charCodeAt(i);
        if (c < 128) {
          out[p++] = c;
        } else if (c < 2048) {
          out[p++] = (c >> 6) | 192;
          out[p++] = (c & 63) | 128;
        } else if (
            ((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
            ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
          // Surrogate Pair
          c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
          out[p++] = (c >> 18) | 240;
          out[p++] = ((c >> 12) & 63) | 128;
          out[p++] = ((c >> 6) & 63) | 128;
          out[p++] = (c & 63) | 128;
        } else {
          out[p++] = (c >> 12) | 224;
          out[p++] = ((c >> 6) & 63) | 128;
          out[p++] = (c & 63) | 128;
        }
        return out;
      };
    
    
      module.exports = FormData;
    
  • utils/mimeMap.js

      module.exports = {
        "0.001": "application/x-001",
        "0.323": "text/h323",
        "0.907": "drawing/907",
        ".acp": "audio/x-mei-aac",
        ".aif": "audio/aiff",
        ".aiff": "audio/aiff",
        ".asa": "text/asa",
        ".asp": "text/asp",
        ".au": "audio/basic",
        ".awf": "application/vnd.adobe.workflow",
        ".bmp": "application/x-bmp",
        ".c4t": "application/x-c4t",
        ".cal": "application/x-cals",
        ".cdf": "application/x-netcdf",
        ".cel": "application/x-cel",
        ".cg4": "application/x-g4",
        ".cit": "application/x-cit",
        ".cml": "text/xml",
        ".cmx": "application/x-cmx",
        ".crl": "application/pkix-crl",
        ".csi": "application/x-csi",
        ".cut": "application/x-cut",
        ".dbm": "application/x-dbm",
        ".dcd": "text/xml",
        ".der": "application/x-x509-ca-cert",
        ".dib": "application/x-dib",
        ".doc": "application/msword",
        ".drw": "application/x-drw",
        ".dwf": "Model/vnd.dwf",
        ".dwg": "application/x-dwg",
        ".dxf": "application/x-dxf",
        ".emf": "application/x-emf",
        ".ent": "text/xml",
        ".eps": "application/x-ps",
        ".etd": "application/x-ebx",
        ".fax": "image/fax",
        ".fif": "application/fractals",
        ".frm": "application/x-frm",
        ".gbr": "application/x-gbr",
        ".gif": "image/gif",
        ".gp4": "application/x-gp4",
        ".hmr": "application/x-hmr",
        ".hpl": "application/x-hpl",
        ".hrf": "application/x-hrf",
        ".htc": "text/x-component",
        ".html": "text/html",
        ".htx": "text/html",
        ".ico": "image/x-icon",
        ".iff": "application/x-iff",
        ".igs": "application/x-igs",
        ".img": "application/x-img",
        ".isp": "application/x-internet-signup",
        ".java": "java/*",
        ".jpe": "image/jpeg",
        ".jpeg": "image/jpeg",
        ".jpg": "application/x-jpg",
        ".jsp": "text/html",
        ".lar": "application/x-laplayer-reg",
        ".lavs": "audio/x-liquid-secure",
        ".lmsff": "audio/x-la-lms",
        ".ltr": "application/x-ltr",
        ".m2v": "video/x-mpeg",
        ".m4e": "video/mpeg4",
        ".man": "application/x-troff-man",
        ".mdb": "application/msaccess",
        ".mfp": "application/x-shockwave-flash",
        ".mhtml": "message/rfc822",
        ".mid": "audio/mid",
        ".mil": "application/x-mil",
        ".mnd": "audio/x-musicnet-download",
        ".mocha": "application/x-javascript",
        ".mp1": "audio/mp1",
        ".mp2v": "video/mpeg",
        ".mp4": "video/mpeg4",
        ".mpd": "application/vnd.ms-project",
        ".mpeg": "video/mpg",
        ".mpga": "audio/rn-mpeg",
        ".mps": "video/x-mpeg",
        ".mpv": "video/mpg",
        ".mpw": "application/vnd.ms-project",
        ".mtx": "text/xml",
        ".net": "image/pnetvue",
        ".nws": "message/rfc822",
        ".out": "application/x-out",
        ".p12": "application/x-pkcs12",
        ".p7c": "application/pkcs7-mime",
        ".p7r": "application/x-pkcs7-certreqresp",
        ".pc5": "application/x-pc5",
        ".pcl": "application/x-pcl",
        ".pdf": "application/pdf",
        ".pdx": "application/vnd.adobe.pdx",
        ".pgl": "application/x-pgl",
        ".pko": "application/vnd.ms-pki.pko",
        ".plg": "text/html",
        ".plt": "application/x-plt",
        ".png": "application/x-png",
        ".ppa": "application/vnd.ms-powerpoint",
        ".pps": "application/vnd.ms-powerpoint",
        ".ppt": "application/x-ppt",
        ".prf": "application/pics-rules",
        ".prt": "application/x-prt",
        ".ps": "application/postscript",
        ".pwz": "application/vnd.ms-powerpoint",
        ".ra": "audio/vnd.rn-realaudio",
        ".ras": "application/x-ras",
        ".rdf": "text/xml",
        ".red": "application/x-red",
        ".rjs": "application/vnd.rn-realsystem-rjs",
        ".rlc": "application/x-rlc",
        ".rm": "application/vnd.rn-realmedia",
        ".rmi": "audio/mid",
        ".rmm": "audio/x-pn-realaudio",
        ".rms": "application/vnd.rn-realmedia-secure",
        ".rmx": "application/vnd.rn-realsystem-rmx",
        ".rp": "image/vnd.rn-realpix",
        ".rsml": "application/vnd.rn-rsml",
        ".rtf": "application/msword",
        ".rv": "video/vnd.rn-realvideo",
        ".sat": "application/x-sat",
        ".sdw": "application/x-sdw",
        ".slb": "application/x-slb",
        ".slk": "drawing/x-slk",
        ".smil": "application/smil",
        ".snd": "audio/basic",
        ".sor": "text/plain",
        ".spl": "application/futuresplash",
        ".ssm": "application/streamingmedia",
        ".stl": "application/vnd.ms-pki.stl",
        ".sty": "application/x-sty",
        ".swf": "application/x-shockwave-flash",
        ".tg4": "application/x-tg4",
        ".tif": "image/tiff",
        ".tiff": "image/tiff",
        ".top": "drawing/x-top",
        ".tsd": "text/xml",
        ".uin": "application/x-icq",
        ".vcf": "text/x-vcard",
        ".vdx": "application/vnd.visio",
        ".vpg": "application/x-vpeg005",
        ".vsd": "application/x-vsd",
        ".vst": "application/vnd.visio",
        ".vsw": "application/vnd.visio",
        ".vtx": "application/vnd.visio",
        ".wav": "audio/wav",
        ".wb1": "application/x-wb1",
        ".wb3": "application/x-wb3",
        ".wiz": "application/msword",
        ".wk4": "application/x-wk4",
        ".wks": "application/x-wks",
        ".wma": "audio/x-ms-wma",
        ".wmf": "application/x-wmf",
        ".wmv": "video/x-ms-wmv",
        ".wmz": "application/x-ms-wmz",
        ".wpd": "application/x-wpd",
        ".wpl": "application/vnd.ms-wpl",
        ".wr1": "application/x-wr1",
        ".wrk": "application/x-wrk",
        ".ws2": "application/x-ws",
        ".wsdl": "text/xml",
        ".xdp": "application/vnd.adobe.xdp",
        ".xfd": "application/vnd.adobe.xfd",
        ".xhtml": "text/html",
        ".xls": "application/x-xls",
        ".xml": "text/xml",
        ".xq": "text/xml",
        ".xquery": "text/xml",
        ".xsl": "text/xml",
        ".xwd": "application/x-xwd",
        ".sis": "application/vnd.symbian.install",
        ".x_t": "application/x-x_t",
        ".apk": "application/vnd.android.package-archive",
        "0.301": "application/x-301",
        "0.906": "application/x-906",
        ".a11": "application/x-a11",
        ".ai": "application/postscript",
        ".aifc": "audio/aiff",
        ".anv": "application/x-anv",
        ".asf": "video/x-ms-asf",
        ".asx": "video/x-ms-asf",
        ".avi": "video/avi",
        ".biz": "text/xml",
        ".bot": "application/x-bot",
        ".c90": "application/x-c90",
        ".cat": "application/vnd.ms-pki.seccat",
        ".cdr": "application/x-cdr",
        ".cer": "application/x-x509-ca-cert",
        ".cgm": "application/x-cgm",
        ".class": "java/*",
        ".cmp": "application/x-cmp",
        ".cot": "application/x-cot",
        ".crt": "application/x-x509-ca-cert",
        ".css": "text/css",
        ".dbf": "application/x-dbf",
        ".dbx": "application/x-dbx",
        ".dcx": "application/x-dcx",
        ".dgn": "application/x-dgn",
        ".dll": "application/x-msdownload",
        ".dot": "application/msword",
        ".dtd": "text/xml",
        ".dwf": "application/x-dwf",
        ".dxb": "application/x-dxb",
        ".edn": "application/vnd.adobe.edn",
        ".eml": "message/rfc822",
        ".epi": "application/x-epi",
        ".eps": "application/postscript",
        ".exe": "application/x-msdownload",
        ".fdf": "application/vnd.fdf",
        ".fo": "text/xml",
        ".g4": "application/x-g4",
        ".tif": "image/tiff",
        ".gl2": "application/x-gl2",
        ".hgl": "application/x-hgl",
        ".hpg": "application/x-hpgl",
        ".hqx": "application/mac-binhex40",
        ".hta": "application/hta",
        ".htm": "text/html",
        ".htt": "text/webviewhtml",
        ".icb": "application/x-icb",
        ".ico": "application/x-ico",
        ".ig4": "application/x-g4",
        ".iii": "application/x-iphone",
        ".ins": "application/x-internet-signup",
        ".IVF": "video/x-ivf",
        ".jfif": "image/jpeg",
        ".jpe": "application/x-jpe",
        ".jpg": "image/jpeg",
        ".js": "application/x-javascript",
        ".la1": "audio/x-liquid-file",
        ".latex": "application/x-latex",
        ".lbm": "application/x-lbm",
        ".ls": "application/x-javascript",
        ".m1v": "video/x-mpeg",
        ".m3u": "audio/mpegurl",
        ".mac": "application/x-mac",
        ".math": "text/xml",
        ".mdb": "application/x-mdb",
        ".mht": "message/rfc822",
        ".mi": "application/x-mi",
        ".midi": "audio/mid",
        ".mml": "text/xml",
        ".mns": "audio/x-musicnet-stream",
        ".movie": "video/x-sgi-movie",
        ".mp2": "audio/mp2",
        ".mp3": "audio/mp3",
        ".mpa": "video/x-mpg",
        ".mpe": "video/x-mpeg",
        ".mpg": "video/mpg",
        ".mpp": "application/vnd.ms-project",
        ".mpt": "application/vnd.ms-project",
        ".mpv2": "video/mpeg",
        ".mpx": "application/vnd.ms-project",
        ".mxp": "application/x-mmxp",
        ".nrf": "application/x-nrf",
        ".odc": "text/x-ms-odc",
        ".p10": "application/pkcs10",
        ".p7b": "application/x-pkcs7-certificates",
        ".p7m": "application/pkcs7-mime",
        ".p7s": "application/pkcs7-signature",
        ".pci": "application/x-pci",
        ".pcx": "application/x-pcx",
        ".pdf": "application/pdf",
        ".pfx": "application/x-pkcs12",
        ".pic": "application/x-pic",
        ".pl": "application/x-perl",
        ".pls": "audio/scpls",
        ".png": "image/png",
        ".pot": "application/vnd.ms-powerpoint",
        ".ppm": "application/x-ppm",
        ".ppt": "application/vnd.ms-powerpoint",
        ".pr": "application/x-pr",
        ".prn": "application/x-prn",
        ".ps": "application/x-ps",
        ".ptn": "application/x-ptn",
        ".r3t": "text/vnd.rn-realtext3d",
        ".ram": "audio/x-pn-realaudio",
        ".rat": "application/rat-file",
        ".rec": "application/vnd.rn-recording",
        ".rgb": "application/x-rgb",
        ".rjt": "application/vnd.rn-realsystem-rjt",
        ".rle": "application/x-rle",
        ".rmf": "application/vnd.adobe.rmf",
        ".rmj": "application/vnd.rn-realsystem-rmj",
        ".rmp": "application/vnd.rn-rn_music_package",
        ".rmvb": "application/vnd.rn-realmedia-vbr",
        ".rnx": "application/vnd.rn-realplayer",
        ".rpm": "audio/x-pn-realaudio-plugin",
        ".rt": "text/vnd.rn-realtext",
        ".rtf": "application/x-rtf",
        ".sam": "application/x-sam",
        ".sdp": "application/sdp",
        ".sit": "application/x-stuffit",
        ".sld": "application/x-sld",
        ".smi": "application/smil",
        ".smk": "application/x-smk",
        ".sol": "text/plain",
        ".spc": "application/x-pkcs7-certificates",
        ".spp": "text/xml",
        ".sst": "application/vnd.ms-pki.certstore",
        ".stm": "text/html",
        ".svg": "text/xml",
        ".tdf": "application/x-tdf",
        ".tga": "application/x-tga",
        ".tif": "application/x-tif",
        ".tld": "text/xml",
        ".torrent": "application/x-bittorrent",
        ".txt": "text/plain",
        ".uls": "text/iuls",
        ".vda": "application/x-vda",
        ".vml": "text/xml",
        ".vsd": "application/vnd.visio",
        ".vss": "application/vnd.visio",
        ".vst": "application/x-vst",
        ".vsx": "application/vnd.visio",
        ".vxml": "text/xml",
        ".wax": "audio/x-ms-wax",
        ".wb2": "application/x-wb2",
        ".wbmp": "image/vnd.wap.wbmp",
        ".wk3": "application/x-wk3",
        ".wkq": "application/x-wkq",
        ".wm": "video/x-ms-wm",
        ".wmd": "application/x-ms-wmd",
        ".wml": "text/vnd.wap.wml",
        ".wmx": "video/x-ms-wmx",
        ".wp6": "application/x-wp6",
        ".wpg": "application/x-wpg",
        ".wq1": "application/x-wq1",
        ".wri": "application/x-wri",
        ".ws": "application/x-ws",
        ".wsc": "text/scriptlet",
        ".wvx": "video/x-ms-wvx",
        ".xdr": "text/xml",
        ".xfdf": "application/vnd.adobe.xfdf",
        ".xls": "application/vnd.ms-excel",
        ".xlw": "application/x-xlw",
        ".xpl": "audio/scpls",
        ".xql": "text/xml",
        ".xsd": "text/xml",
        ".xslt": "text/xml",
        ".x_b": "application/x-x_b",
        ".sisx": "application/vnd.symbian.install",
        ".ipa": "application/vnd.iphone",
        ".xap": "application/x-silverlight-app",
        ".zip": "application/x-zip-compressed",
      }
    
posted @ 2024-11-25 14:05  不完美的完美  阅读(1)  评论(0编辑  收藏  举报