本地分块上传文件

注意,该功能已经下架。

下架原因:该功能用量少,没有适合场景。

实现原理:通过 UDP 上传数据,上传数据的时候,将文件切割,然后上传固定长度的数据。

wxml 文件

<view class="page">
	<view class="weui-form">
		<view class="weui-form__bd">
			<view class="weui-form__text-area">
				<h2 class="weui-form__title">分块上传视频文件</h2>
				<view class="weui-form__desc">打开手机WIFI后,可将本手机的视频文件上传到局域网内的服务器。</view>
			</view>
			<view class="weui-form__control-area">
				<view class="weui-cells__group weui-cells__group_form">
					<view class="weui-cells__title">地址信息</view>
					<view class="weui-cells">
						<view class="weui-cell weui-cell_active">
							<view class="weui-cell__hd"><label class="weui-label">UDP地址</label></view>
							<view class="weui-cell__bd">
								<input class="weui-input" placeholder="IP地址:端口号" placeholder-class="weui-input__placeholder"  name="addr" bindblur="getAddr" />
							</view>
						</view>

					</view>
					<view class="weui-cells__tips weui-cells__tips_warn " style="{{isErr ? 'display:block' : 'display:none'}}">错误提示:{{tips}}</view>
				</view>
				<view class="weui-cells__group weui-cells__group_form">
					<view class="weui-cells__title">文件信息</view>
					<view class="weui-cells weui-cells_form">
						<view class="weui-cell weui-cell_uploader">
							<view class="weui-cell__bd">
								<view class="weui-uploader">
									<view class="weui-uploader__hd">
										<view aria-role="option" class="weui-uploader__overview">
											<view class="weui-uploader__title">选择视频文件</view>

										</view>
										<view class="weui-uploader__tips">
											请选择单个哟
										</view>
									</view>
									<view class="weui-uploader__bd">
										<view class="weui-uploader__files" id="uploaderFiles">
											<block wx:for="{{files}}" wx:key="*this">
												<view class="weui-uploader__file" bindtap="viewVideo" id="{{item}}">
													<video class="weui-uploader__img" src="{{item}}" />
												</view>
											</block>
										</view>
										<view class="weui-uploader__input-box">
											<view aria-ro le="button" aria-label="选择视频" class="weui-uploader__input" bindtap="getVideo"></view>
										</view>
									</view>
								</view>
							</view>
						</view>
					</view>
				</view>

				<view class="weui-cells__group weui-cells__group_form">
					<view class="weui-cells__title">传输信息</view>
					<view class="weui-cells weui-cells_form">
						<view class="weui-cell" hover-class="weui-cell_active">
							<view class="weui-cell__bd">
								<progress percent="{{pi}}" stroke-width="3" show-info="true"/>
							</view>
						</view>
					</view>
				</view>
			</view>
		</view>
		<view class="weui-form__ft">
			<view class="weui-form__tips-area">
				<view class="weui-form__tips">
					{{tips}}
				</view>
			</view>
			<view class="weui-form__opr-area">
				<button aria-role="button" class="weui-btn weui-btn_primary" bind:tap="doTransFile">上传文件</button>
			</view>
		</view>
	</view>
</view>

wxss 文件

.weui-form {
	padding-top: 64rpx;
}

.page{
	height: 100%;
}

js 文件

const utils = require("../../utils/utils.js");
import MyUdp from './myUdp.js';

const UdpPacketSize = 1038; // 单位为字节
const UdpContentSize = 1024; // 单位为字节

var cu = null; // udp句柄
var buf = null; // 缓存
var bufidx = 0; // 缓存索引
var sendCnt = 0; // 发送计数
var pieceIdx = 0; // 块计数
var pkgIdx = 0; // 包计数

Page({

  /**
   * 页面的初始数据
   */
  data: {
    ip: '',
    port: 0,
    files: [],
    pi: 0,
    fileType: '',
    fileSize: 0,
    cacheSize: 0,
    pieceCount: 0,
  },


  // 选择视频
  getVideo() {
    const that = this;
    wx.chooseMedia({
      count: 1,
      mediaType: ['video'],
      sourceType: ['album'],
      sizeType: ['original'],
      success(res) {
        var tempFiles = res.tempFiles;
        const tfp = tempFiles[0].tempFilePath;
        let fileType = utils.getExtension(tfp);
        let myfiles = [];
        myfiles.push(tfp);
        let fz = tempFiles[0].size;
        let bk1 = Math.ceil(fz / UdpContentSize);
        let bk2 = bk1 * UdpContentSize;
        console.log('文件大小,', fz, bk2);
        that.setData({
          files: myfiles,
          fileSize: fz,
          fileType: fileType,
          pieceCount: bk1,
          cacheSize: bk2,
          pi: 0,
        });

        console.log('debug,', that.data.files, that.data.fileSize, that.data.fileType)
        if (!utils.isEmpty(cu)) {
          cu.close()
        }
        cu = null; // udp句柄
        buf = null; // 缓存
        bufidx = 0; // 缓存索引
        sendCnt = 0; // 发送计数
        pieceIdx = 0; // 块计数
        pkgIdx = 0; // 包计数
      },
    });
  },

  // 预览视频
  viewVideo(e) {
    wx.previewMedia({
      current: e.currentTarget.id,
      sources: [{
        url: this.data.files[0],
        type: 'video',
      }],
    });
  },

  getAddr(e) {
    const that = this;
    let obj = e.detail.value;
    console.log('addr,', obj);
    const objArr = utils.str2arr3(obj);
    that.setData({
      ip: objArr[0],
      port: Number(objArr[1]),
    })
  },

  // 获取文件内容
  getFileContent() {
    const that = this;
    try {
      const filePath = that.data.files[0];
      const fs = wx.getFileSystemManager()
      const res = fs.readFileSync(filePath)
      console.log(res.byteLength);
      return res;
    } catch (e) {
      console.error(e)
    }
  },

  // udp 错误监听
  uErr(res) {
    console.log('udp err,', res.errMsg);
    const that = this;
    // 关闭udp
    cu.close();
  },

  // UDP消息
  uMsg(res) {
    // console.log('recv msg,', res);
    const that = this;
    const msg = res.message;
    const mdv = new DataView(msg);
    const tag = mdv.getUint16(0, true);
    // console.log('tag,', tag);
    // 收到确认数据
    if (tag == 0x0002) {
      // console.log('t1,',pieceIdx,that.data.pieceCount)
      if (pieceIdx >= that.data.pieceCount) {
        console.log('已经发送完毕');
        sendCnt = sendCnt + 1;
        pkgIdx = pkgIdx + 1;
        const ab = new ArrayBuffer(10);
        const dv = new DataView(ab);
        dv.setUint16(0, 0x0005, true);
        dv.setUint32(2, 4, true);
        dv.setUint32(6, pkgIdx, true);
        cu.send(that.data.ip, that.data.port, ab);
      } else {
        pkgIdx = pkgIdx + 1;
        const ab = new ArrayBuffer(UdpPacketSize);
        const dv = new DataView(ab);
        dv.setUint16(0, 0x0003, true);
        dv.setUint32(2, 1032, true);
        dv.setUint32(6, pkgIdx, true);
        dv.setUint32(10, 0, true);
        // 复制内容
        // console.log('t2,',bufidx,that.data.cacheSize)
        if (bufidx <= that.data.cacheSize) {
          utils.copyBuffer(ab, 14, buf, bufidx, bufidx + UdpContentSize);
          bufidx = bufidx + UdpContentSize;
          cu.send(that.data.ip, that.data.port, ab);
        }

        // 计数
        sendCnt = sendCnt + 1;
        pieceIdx = pieceIdx + 1;
        let pi = Math.ceil(pieceIdx / that.data.pieceCount * 100);
        that.setData({
          pi: pi,
        })
      }

    }
  },

  doTransFile() {
    const that = this;
    console.log("开始分块上传文件");
    console.log("文件大小为,", that.data.fileSize);
    console.log("文件缓存为,", that.data.cacheSize);
    console.log("文件被分成切片数量,", that.data.pieceCount);
    console.log("当前时间为,", utils.getNowStr());
    if (utils.isEmpty(that.data.files)) {
      wx.showToast({
        title: '请选择文件',
      })
      return
    }

    // 初始化udp
    cu = new MyUdp();
    cu.setup(that.uMsg, that.uErr)

    bufidx = 0;
    buf = new ArrayBuffer(that.data.cacheSize);
    const fileContent = that.getFileContent();
    utils.copyBuffer(buf, 0, fileContent, 0, that.data.fileSize);
    // u32 文件大小 u32 缓存大小 u32 文件类型 u32 切片数量
    sendCnt = sendCnt + 1;
    pkgIdx = 1
    let fileTypeCode = utils.getFileTypeCode(that.data.fileType);
    let ab = new ArrayBuffer(30);
    let dv = new DataView(ab);
    dv.setUint16(0, 0x0001, true);
    dv.setUint32(2, 24, true);
    dv.setUint32(6, pkgIdx, true);
    dv.setUint32(10, 0, true);
    dv.setUint32(14, that.data.fileSize, true);
    dv.setUint32(18, that.data.cacheSize, true);
    dv.setUint32(22, fileTypeCode, true);
    dv.setUint32(26, that.data.pieceCount, true);
    // console.log('ab,', ab, ab.byteLength);
    cu.send(that.data.ip, that.data.port, ab);
    console.log('send start');
  },
})