基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件

目录
  • 1. 前言
  • 2. 关于vue-simple-uploader
  • 3. 基于vue-simple-uploader封装全局上传组件
  • 4. 文件上传流程概览
  • 5. 文件分片
  • 6. MD5的计算过程
  • 7. 秒传及断点续传
    • 7.1 对于前端来说
    • 7.2 前端做分片检验:checkChunkUploadedByResponse
  • 8. 自己加的一些功能
    • 新增了自定义的状态
    • 优化插件调用
    • file的动态params绑定
  • 9. 源码整理
  • 10. vue-simple-uploader 常见问题整理

1. 前言

之前公司要在管理系统中做一个全局上传插件,即切换各个页面的时候,上传界面还在并且上传不会受到影响,这在vue这种spa框架面前并不是什么难题。然而后端大佬说我们要实现分片上传秒传以及断点续传的功能,听起来头都大了。

很久之前我写了一篇webuploader的文章,结果使用起来发现问题很多,且官方团队不再维护这个插件了, 经过多天调研及踩雷,最终决定基于vue-simple-uploader插件实现该功能,在项目中使用起来无痛且稳定。

如果你只是想实现基本的(非定制化的)上传功能,直接使用vue-simple-uploader,多读一下它的文档,不需要更多的二次封装。
如果你只是想实现全局上传插件,也可以参照一下我的实现。
如果你用到了分片上传、秒传及断点续传这些复杂的功能,恭喜你,这篇文章的重点就在于此。

本文源码在此:http://github.com/shady-xia/vue-uploader-solutions

2. 关于vue-simple-uploader

vue-simple-uploader是基于 simple-uploader.js 封装的vue上传插件。它的优点包括且不限于以下几种:

  • 支持文件、多文件、文件夹上传;支持拖拽文件、文件夹上传
  • 可暂停、继续上传
  • 错误处理
  • 支持“秒传”,通过文件判断服务端是否已存在从而实现“秒传”
  • 分块上传
  • 支持进度、预估剩余时间、出错自动重试、重传等操作

读这篇文章之前,建议先读一遍simple-uploader.js的文档,然后再读一下vue-simple-uploader的文档,了解一下各个参数的作用是什么,我在这里假定大家已经比较熟悉了。。
vue-simple-uploader文档

simple-uploader.js文档

安装npm install vue-simple-uploader --save
使用:在main.js中:

import uploader from 'vue-simple-uploader'
Vue.use(uploader)

3. 基于vue-simple-uploader封装全局上传组件

引入vue-simple-uploader后,我们开始封装全局的上传组件globalUploader.vue,代码比较长,就不整个放出来了,源码放到github上了,这里一步一步地讲解。

template部分如下,本人自定义了模板和样式,所以html部分比较长,css部分暂时不列出,大家可以根据自己的ui去更改,主要关注一下uploader这个组件的options参数及文件addedsuccessprogresserror几个事件:

<template>
    <div id="global-uploader">

        <!-- 上传 -->
        <uploader
                ref="uploader"
                :options="options"
                :autoStart="false"
                @file-added="onFileAdded"
                @file-success="onFileSuccess"
                @file-progress="onFileProgress"
                @file-error="onFileError"
                class="uploader-app">
            <uploader-unsupport></uploader-unsupport>

            <uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn">选择文件</uploader-btn>

            <uploader-list v-show="panelShow">
                <div class="file-panel" slot-scope="props" :class="{'collapse': collapse}">
                    <div class="file-title">
                        <h2>文件列表</h2>
                        <div class="operate">
                            <el-button @click="fileListShow" type="text" :title="collapse ? '展开':'折叠' ">
                                <i class="iconfont" :class="collapse ? 'icon-fullscreen': 'icon-minus-round'"></i>
                            </el-button>
                            <el-button @click="close" type="text" title="关闭">
                                <i class="iconfont icon-close"></i>
                            </el-button>
                        </div>
                    </div>

                    <ul class="file-list">
                        <li class="file-item" :class="`file-${file.id}`" v-for="file in props.fileList" :key="file.id">
                            <uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
                        </li>
                        <div class="no-file" v-if="!props.fileList.length"><i class="nucfont inuc-empty-file"></i> 暂无待上传文件</div>
                    </ul>
                </div>
            </uploader-list>

        </uploader>

    </div>
</template>

组件中的data部分

data() {
    return {
        options: {
            target: 'http://xxxxx/xx', // 目标上传 URL
            chunkSize: '2048000',   //分块大小
            fileParameterName: 'file', //上传文件时文件的参数名,默认file
            maxChunkRetries: 3,  //最大自动失败重试上传次数
            testChunks: true,     //是否开启服务器分片校验
            // 服务器分片校验函数,秒传及断点续传基础
            checkChunkUploadedByResponse: function (chunk, message) {
                let objMessage = JSON.parse(message);
                if (objMessage.skipUpload) {
                    return true;
                }

                return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0
            },
            // 额外的自定义查询参数
            query: (file, chunk) => {
               return {
                  ...file.params, 
                }
            },

        },
        attrs: {
         // 接受的文件类型,形如['.png', '.jpg', '.jpeg', '.gif', '.bmp'...] 这里我封装了一下
            accept: ACCEPT_CONFIG.getAll()
        },
        panelShow: false,   //选择文件后,展示上传panel
    }
},

全局引用:
app.vue中引用,即作为全局的组件一直存在,只不过在不使用的时候把上传界面隐藏了

<global-uploader></global-uploader>

4. 文件上传流程概览

1. 点击按钮,触发文件上传操作:

(如果你做的不是全局上传的功能,而是直接点击上传,忽略这一步。)

因为我做的是全局上传的插件,要先把上传的窗口隐藏起来,在点击某个上传按钮的时候,用Bus发送一个openUploader的事件,在globalUploader.vue中接收该事件,trigger我们uploader-btn的click事件。

在某个页面中,点击上传按钮,同时把要给后台的参数带过来(如果有的话),这里组件之间传值我用的event bus,当然用vuex会更好:

Bus.$emit('openUploader', {
   superiorID: this.superiorID
})

globalUploader.vue中接收该事件:

Bus.$on('openUploader', query => {
    this.params = query || {};

    // 这样就打开了选择文件的操作窗口
    if (this.$refs.uploadBtn) { 
        this.$refs.uploadBtn.$el.click()
    }
});

2. 选择文件后,将上传的窗口展示出来,开始md5的计算工作

onFileAdded(file) {
    this.panelShow = true
    Bus.$emit('fileAdded')

    // 将额外的参数赋值到每个文件上,以不同文件使用不同params的需求
    file.params = this.params

    // 计算MD5,完成后开始上传操作
    this.computeMD5(file).then((result) => this.startUpload(result))
},

这里有个前提,我在uploader中将autoStart设为了false,为什么要这么做?

在选择文件之后,我要计算MD5,以此来实现断点续传及秒传的功能,所以选择文件后直接开始上传肯定不行,要等MD5计算完毕之后,再开始文件上传的操作。

具体的MD5计算方法,会在下面讲,这里先简单引出。

上传过程中,会不断触发file-progress上传进度的回调

// 文件进度的回调
onFileProgress(rootFile, file, chunk) {
    console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
},

3. 文件上传成功后
文件上传成功后,在“上传完成”的回调中,通过服务端返回的needMerge字段,来判断是否需要再发送合并分片的请求,
如果这个字段为true,则需要给后台发一个请求合并的ajax请求,否则直接上传成功。

注意:这里的needMerge是我和后台商议决定的字段名

onFileSuccess(rootFile, file, response, chunk) {
    let res = JSON.parse(response);

    // 服务器自定义的错误,这种错误是Uploader无法拦截的
    if (!res.result) {
        this.$message({ message: res.message, type: 'error' });
        return
    }
	
	// 如果服务端返回需要合并
    if (res.needMerge) {
        api.mergeSimpleUpload({
            tempName: res.tempName,
            fileName: file.name,
            ...file.params,
        }).then(data => {
            // 文件合并成功
            Bus.$emit('fileSuccess', data);
        }).catch(e => {});
    // 不需要合并    
    } else {
        Bus.$emit('fileSuccess', res);
        console.log('上传成功');
    }
},

onFileError(rootFile, file, response, chunk) {
	console.log(error)
},

5. 文件分片

vue-simple-uploader自动将文件进行分片,在optionschunkSize中可以设置每个分片的大小。

如图:对于大文件来说,会发送多个请求,在设置testChunkstrue后(在插件中默认就是true),会发送与服务器进行分片校验的请求,下面的第一个get请求就是该请求;后面的每一个post请求都是上传分片的请求

看一下发送给服务端的参数,其中chunkNumber表示当前是第几个分片,totalChunks代表所有的分片数,这两个参数都是都是插件根据你设置的chunkSize来计算的。

需要注意的就是在最后文件上传成功的事件中,通过后台返回的字段,来判断是否要再给后台发送一个文件合并的请求。

6. MD5的计算过程

断点续传及秒传的基础是要计算文件的MD5,这是文件的唯一标识,然后服务器根据MD5进行判断,是进行秒传还是断点续传。

file-added事件之后,就计算MD5,我们最终的目的是将计算出来的MD5加到参数里传给后台,然后继续文件上传的操作,详细的思路步骤是:

  1. 把uploader组件的autoStart设为false,即选择文件后不会自动开始上传
  2. 先通过 file.pause()暂停文件,然后通过H5的FileReader接口读取文件
  3. 将异步读取文件的结果进行MD5,这里我用的加密工具是spark-md5,你可以通过npm install spark-md5 --save来安装,也可以使用其他MD5加密工具。
  4. file有个属性是uniqueIdentifier,代表文件唯一标示,我们把计算出来的MD5赋值给这个属性 file.uniqueIdentifier = md5,这就实现了我们最终的目的。
  5. 通过file.resume()开始/继续文件上传。
/**
 * 计算md5值,以实现断点续传及秒传
 * @param file
 * @returns Promise
 */
computeMD5(file) {
  let fileReader = new FileReader()
  let time = new Date().getTime()
  let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
  let currentChunk = 0
  const chunkSize = 10 * 1024 * 1000
  let chunks = Math.ceil(file.size / chunkSize)
  let spark = new SparkMD5.ArrayBuffer()

  // 文件状态设为"计算MD5"
  this.statusSet(file.id, 'md5')
  file.pause()

  // 计算MD5时隐藏”开始“按钮
  this.$nextTick(() => {
    document.querySelector(`.file-${file.id} .uploader-file-resume`).style.display = 'none'
  })

  loadNext()

  return new Promise((resolve, reject) => {
    fileReader.onload = (e) => {
      spark.append(e.target.result)

      if (currentChunk < chunks) {
        currentChunk++
        loadNext()

        // 实时展示MD5的计算进度
        this.$nextTick(() => {
          const md5ProgressText ='校验MD5 '+ ((currentChunk/chunks)*100).toFixed(0)+'%'
          document.querySelector(`.custom-status-${file.id}`).innerText = md5ProgressText
        })

      } else {
        let md5 = spark.end()

        // md5计算完毕
        resolve({md5, file})

        console.log(
          `MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${
            new Date().getTime() - time
          } ms`
        )
      }
    }

    fileReader.onerror = function () {
      this.error(`文件${file.name}读取出错,请检查该文件`)
      file.cancel()
      reject()
    }
  })

  function loadNext() {
    let start = currentChunk * chunkSize
    let end = start + chunkSize >= file.size ? file.size : start + chunkSize

    fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end))
  }
},

// md5计算完毕,开始上传
startUpload({md5, file}) {
    file.uniqueIdentifier = md5
    file.resume()
    this.statusRemove(file.id)
},

给file的uniqueIdentifier 属性赋值后,请求中的identifier即是我们计算出来的MD5

7. 秒传及断点续传

在计算完MD5后,我们就能谈断点续传及秒传的概念了。

服务器根据前端传过来的MD5去判断是否可以进行秒传或断点续传:

  • a. 服务器发现文件已经完全上传成功,则直接返回秒传的标识。
  • b. 服务器发现文件上传过分片信息,则返回这些分片信息,告诉前端继续上传,即断点续传

7.1 对于前端来说

在每次上传过程的最开始,vue-simple-uploader会发送一个get请求,来问服务器我哪些分片已经上传过了,

这个请求返回的结果也有几种可能:

  • a. 如果是秒传,在请求结果中会有相应的标识,比如我这里是skipUploadtrue,且返回了url,代表服务器告诉我们这个文件已经有了,我直接把url给你,你不用再传了,这就是秒传

图a1:秒传情况下后台返回值

图a2:秒传gif

  • b. 如果后台返回了分片信息,这是断点续传。如图,返回的数据中有个uploaded的字段,代表这些分片是已经上传过的了,插件会自动跳过这些分片的上传。

图b1:断点续传情况下后台返回值

图b2:断点续传gif

  • c. 可能什么都不会返回,那这就是个全新的文件了,走完整的分片上传逻辑

7.2 前端做分片检验:checkChunkUploadedByResponse

前面讲的是概念,现在说一说前端在拿到这些返回值之后怎么处理。
插件自己是不会判断哪个需要跳过的,在代码中由options中的checkChunkUploadedByResponse控制,它会根据 XHR 响应内容检测每个块是否上传成功了,成功的分片直接跳过上传
你要在这个函数中进行处理,可以跳过的情况下返回true即可。

checkChunkUploadedByResponse: function (chunk, message) {
	 let objMessage = JSON.parse(message);
     if (objMessage.skipUpload) {
         return true;
     }

     return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0
},

注:skipUploaduploaded 是我和后台商议的字段,你要按照后台实际返回的字段名来。

8. 自己加的一些功能

后期在项目自用的过程中,经历了很多功能的添加和优化,这里简单的记录一下,看看有没有能帮到大家的。

新增了自定义的状态

(之前一直有小伙伴问怎么没有“校验MD5”,“合并中”这些状态,我就把我的方法写出来了,方式很笨,但是能实现效果)

插件原本只支持了successerroruploadingpausedwaiting这几种状态,不过0.6.0 版本之后,fileStatusText 可以设置为一个函数,但是提供的参数还是不太能满足我的需要。

由于业务需求,我额外增加了“校验MD5”“合并中”“转码中”“上传失败”这几种自定义的状态

由于不能改源码,我用了比较hack的方式:
当自定义状态开始时,要手动调一下statusSet方法,生成一个p标签盖在原本的状态上面;当自定义状态结束时,还要手动调用statusRemove移除该标签。

this.statusSet(file.id, 'merging');
this.statusRemove(file.id);

具体使用可以参考源码。

优化插件调用

通过bus调用的方式,可以自定义发送给服务端的额外参数 params,以及自定义上传选项 options

// params: 发送给服务器的额外参数;
// options:上传选项,目前支持 target、testChunks、mergeFn、accept等
Bus.$emit('openUploader', {params: {}, options: {}})

file的动态params绑定

将动态的paramas放在了onFileAdded事件中,以解决了不同file使用不同params的需求

 onFileAdded(file) {
    this.panelShow = true;
    this.computeMD5(file);

    // 将额外的参数赋值到每个文件上,解决了不同文件使用不同params的需求
    file.params = this.params

    Bus.$emit('fileAdded');
},

同时要修改options的query:

query: (file, chunk) => {
    return {
        ...file.params,
    }
},

9. 源码整理

源码放到github上了:http://github.com/shady-xia/vue-uploader-solutions

预览地址:http://shady-xia.github.io/vue-uploader-solutions

关于源码的一些解析可以看 github 中的 README.

10. vue-simple-uploader 常见问题整理

这篇文章已经太长了, 遂专门开了一篇文章,整理了下vue-simple-uploader 常见的问题:
vue-simple-uploader 常见问题整理

本文转载于网络 如有侵权请联系删除

相关文章

  • 激光电视开启未来,2021年是“大众化”的开始

    文|曾响铃 来源|科技向令说(xiangling0815)电视市场连续多年下行已成不争的事实,2020年突如其来的疫情更是让行业雪上加霜,“量额价三降”为行业又一次敲响警钟。电视行业急需创新这在多年前就是共识,“激光电视”成为行业一致追逐的目标,这不刚步入2021年市场就有了新动态。1月11日,海信发布新款激光电视,号称将激光电视带入全色时代;仅隔一天1月12日坚果投影同样发布新款激光电视U2Pro,谋定激光电视就是未来。从品牌动作来看,大家都在加速向激光电视靠拢,而品牌着力则正让电视行业进入全新时代。1逆时代上扬,激光电视“乘风破浪”这几年电视市场的行情有多不好?看看销量情况便能知一二。据AVC数据显示,2020年上半年国内电视零售量为2089万台,同比下降9.1%;销售额516亿元同比下降22.2%;同时,产品均价也下跌到了2469元,同比下降14.4%。即便到了下半年市场依然没有起色,同样来自AVC数据显示,第三季度中国彩电市场销量990万台,同比下降6.4%,销额261亿元,同比下降7.4%。7-9月更是迎来连续三月的量额齐跌,市场陷入疫情后2020年第二轮下滑周期。自2016

  • 丢人不!还用System.out.println("");太 Low了!

    作者:雨尔辰辰 my.oschina.net/yuchener/blog/46585161、日志框架 小张;开发一个大型系统:1、System.out.println("");将关键数据打印在控制台;去掉?写在一个文件?2、框架来记录系统的一些运行时信息;日志框架;zhanglogging.jar;3、高大上的几个功能?异步模式?自动归档?xxxx?zhanglogging-good.jar?4、将以前框架卸下来?换上新的框架,重新修改之前相关的API;zhanglogging-prefect.jar;5、JDBC---数据库驱动;写了一个统一的接口层;日志门面(日志的一个抽象层);logging-abstract.jar;给项目中导入具体的日志实现就行了;我们之前的日志框架都是实现的抽象层;市面上的日志框架:JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j....左边选一个门面(抽象层)、右边来选一个实现;日志门面:SLF4J;日子实现:Logback;Springboot:底层是Spring框架,Spring框架默

  • VUE 入门基础(6)

    六,条件渲染   v-if添加一个条件块     <h1v-if=“ok”>Yes</h1>   也可以用v-else添加else块  <template>中v-if条件组  因为v-if是一个指令,需要将它添加到一个元素上,但是如果我们想切换多个元素呢可以把一个<template>   元素当做包装元素,并在上面使用v-if,最终的渲染结果不会包括它。    <template>       <h1>Title</h1>       <p>Paragraph1</p>       <p>Paragraph2</p>     </template>复制  v-else     可以用v-else指令给v-if添加一个else块     <divv-if="Math.random()>0.5">       Sorry     </div>     <divv-else>

  • MAC MySQL安装配置

    1.下载 下载地址:https://dev.mysql.com/downloads/mysql/  注意选择对应的版本,M系列芯片对应ARM 2.安装 参考官网教程,点击地址查看, 一直点击继续即可,注意要记住root用户端密码 3.配置  在 ~/.bash_profile  增加  #设置mysql$ exportPATH=/usr/local/mysql/bin:$PATH$复制 4.服务端启动 参考官网教程,点击地址查看 在系统偏好设置中启动MySQL服务 5.客户端连接 打开终端,通过命令连接数据库即可 mysql-uroot-p[密码]复制   参考 InstallingMySQLonmacOS      

  • 《从零开始学Swift》学习笔记(Day67)——Cocoa Touch设计模式及应用之MVC模式

    原创文章,欢迎转载。转载请注明:关东升的博客  MVC(Model-View-Controller,模型-视图-控制器)模式是相当古老的设计模式之一,它最早出现在Smalltalk语言中。现在,很多计算机语言和架构都采用了MVC模式。  MVC模式概述 MVC模式是一种复合设计模式,由“观察者”(Observer)模式、“策略”(Strategy)模式和“合成”(Composite)模式等组成。MVC模式由3个部分组成,如图所示,这3个部分的作用如下所示。   模型。保存应用数据的状态,回应视图对状态的查询,处理应用业务逻辑,完成应用的功能,将状态的变化通知视图。   视图。为用户展示信息并提供接口。用户通过视图向控制器发出动作请求,然后再向模型发出查询状态的申请,而模型状态的变化会通知给视图。   控制器。接收用户请求,根据请求更新模型。另外,控制器还会更新所选择的视图作为对用户请求的回应。控制器是视图和模型的媒介,可以降低视图与模型的耦合度,使视图和模型的权责更加清晰,从而提高开发效率。 &

  • python学习笔记1

    常用保留字 importkeywordkeyword.kwlist python//为取整除,向下取整接近商的整数/除,例如b除以a1<2==2等同于(1<2)and(2==)san=aifa<belseb#替代三元运算符num1,num2=2,4#多个变量赋值 not(4==6)and5+7/2or0 都是True的情况下and会返回后一个,or会返回前一个。都是False的情况下and会返回前一个,or会返回后一个。前面是True后面是False情况下and会返回后一个,or会返回前一个。前面是False后面是True情况下and会返回前一个,or会返回后一个。大体就是and一遇到False立马返回,or一遇到True立马返回。 is和==的区别 is判断a对象是否就是b对象,用于判断两个变量引用对象是否为同一个,是通过id来判断的==判断a对象的值是否和b对象的值相等,是通过value来判断的 列表、元组、集合、字典的区别 列表(list):是长度可变有序的数据存储容器,可以通过下标索引取到相应的数据元组(tuple):固定长度不可变的顺序容器,访问效率高

  • 网络编程(day3)socketserver模块 并发的ftp带上传下载文件功能

    socket实现并发,记住继承关系的几张图 基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环 socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题) 客户端没必要变  socketserver模块已经实现多进程,多线程客户端没有必要动,服务端要动继承必须继承,handle方法也是必须的 http://www.cnblogs.com/linhaifeng/articles/6129246.html#_label14     server端 #服务端第一个特点是: #一直运行提供服务(链接循环),(基于一个链接通信循环) #绑定一个唯一的地址 # #importsocket #phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机 #phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#就是它,在bind前加 #phone.bind(('127.0.0.1',8080))

  • 性能概念,什么是性能?

    1.什么是性能? 性能可以从两个大的指标维度来概括: 1.时间:系统处理请求的时间 2.资源:系统运行过程中资源消耗情况 2.什么是性能测试? 使用工具模拟,不同场景(多种正常、峰值、异常负载条件),对软件的各项性能指标的测试(cpu使用率,内存使用率, 数据库IO,TPS,Responsetime,网络io,文件io,最大支持用户数)  3.性能测试的目的是什么? 1、评估当前系统的性能能力 2、寻找瓶颈,优化性能 3、评估软件性能能否满足未来的需要   3、性能测试的策略有哪些? 1、基准测试  建立一个基准线(按照某些指标系统正常的情况下),当改变系统软硬指标后,再次进行测试,来发现哪些 变化对性能影响的测试 2、负载测试 通过逐渐增加负载,确定在满足系统性能指标情况下,找出系统能承受的最大负载量(响应时间或者某种资源达到饱和)的测试 3、稳定性测试: 系统在稳定运行(正常负载下)情况下,长时间一天或者一周,并最终保证服务器能满足线上业务要求的测试 4、并发测试: 极短时间内,同时请求服务资源(案例:并发) 5、压力测试: 系统负载超过系统预期的最大

  • 开关电源基础知识

    一、开关电源的类型 1、电源的类型   (1)、性稳压器传输元件工作在线性区仅限于降压转换 (2)、开关稳压器传输元件开关,在每个周期完全接通和完全切断包括一个电感器多种拓扑(降压、升压、降压-升压等) (3)、充电泵传输元件开关,有些完全导通,而有些则工作在线性区仅限电容器 2、为什么选用开关电源 从上图可以看出开关电源的热量损失Pd=12*0.61*10%=0.73W 线性稳压电源的热量损失为Pd=(12-3.3)*2=17.4W 3、开关电源VS线性稳压器    (1)、开关电源 能够提升电压(升压) 以及使电压减低(降压)甚至反相     具有较高的效率 (2)线性稳压器 只能实现降压 开关电源 4、什么是开关稳压器 5、什么是PWM   脉冲宽度调制(英文首字母缩写)改变开关的导通与关断时间的简单方法占空比(tON与T之比)但是,我们不能采用一个脉冲输出!需要一种实现&nbs

  • 使用python对py文件程序代码复用度检查

    #!/user/bin/envpython #@Time:2018/6/514:58 #@Author:PGIDYSQ #@File:PyCheck.py fromos.pathimportisfileasisfile fromtimeimporttimeastime Result={} AllLines=[] FileName=r'C:\Users\PGIDYSQ\Desktop\fibo.py'#访问.py文件路径 '''py文件程序代码复用度检查''' defPreOperate(): globalAllLines withopen(FileName,'r',encoding='UTF-8')asfp: forlineinfp: line=''.join(line.split()) AllLines.append(line) defIfHasDuplicated(Index1): foriteminResult.values(): foritinitem: ifIndex1==it[0]: returnit[1] returnFalse defIsInSpan(Index2):

  • 英文和数字组合字符串排序问题(A1,A2,B1,B2)

    今天工作遇到英文和数字组合字符串排序的问题,故在此记录一下 问题看如下代码: varlist=newList<string>(){"B1","B2","A11","A1","A3","A2"}; list=list.OrderBy(a=>a).ToList(); foreach(variteminlist) { Console.Write(item+";"); }复制 得到的结果: A1;A11;A2;A3;B1;B2;复制 想要实现的效果是A11排在A3之后,但是通过默认排序A11却处于A1和A2之间;故满足不了需求。 为了满足需求,便需要自定义排序方法 排序方法代码如下: publicclassSemiNumericComparer:IComparer<string> { publicintCompare(stringx,stringy) { try { varxc=GetHaveCharacter(x); varyc=GetHaveCharacter(y); if(string.Compare(xc,yc,false)==0) { varn

  • MBRLock

    某样本中提取 ;===============SUBROUTINE======================================= start_7C03procfar;CODEXREF:j_start_7C03↑j ;start_7C03+B8↓j movax,cs movds,ax movss,ax moves,ax movsp,100h movbp,7CEDh;Yourdiskhavealock!!!Pleaseentertheunlockpassword movbx,7CEDh callstrlen_C7 movcx,ax;getnote'lenstocx movax,1301h movbx,0Ch movdl,0 int10h;-VIDEO-WRITESTRING(AT,XT286,PS,EGA,VGA) ;AL=mode,BL=attributeifALbit1clear,BH=displaypagenumber ;DH,DL=row,columnofstartingcursorposition,CX=lengthofstring ;ES:BP->s

  • 安装tcmalloc

    安装google-perftools:#tarzxvfgoogle-perftools-1.6.tar.gz #cdgoogle-perftools-1.6 #./configure#make#makeinstall 3.运行以下二行命令 echo"/usr/local/lib"> /etc/ld.so.conf.d/usr_local_lib.conf/sbin/ldconfig

  • js函数

    函数 函数定义时小括号里的参数是形参函数调用时小括号里的实际参数是实参 函数加小括号1.可以调用函数2.可以接收函数内部返回的值(functionadd(a,b){       returna+b;}  var sum=add(a,b);  console.log(sum);) 变量作用域:指的是变量可被访问的范围,分为 全局作用域 和  局部作用域全局作用域:一个html文档只有一个全局作用域局部作用域:也叫做函数作用域,指的是在函数内部的区域声明在全局作用域的变量就叫做全局变量,声明在局部作用域的变量 全局变量在任何地方都可以访问 局部变量在函数内部才可以访问局部变量不能互相访问,只能自己访问自己 函数作用域 函数提升:只针对函数声明的方式 function fn(){}(有函数提升) var fn=function(){}(没有函数提升)  ES5匿名函数function(){} 立即执行函数

  • Day 68

    第538题: 给定一个二叉搜索树(BinarySearchTree),把它转换成为累加树(GreaterTree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。来源:力扣(LeetCode)   1、二叉搜索树,可知它的中序遍历是升序的,那么它的中序遍历反过来就是一个降序的序列;   而转换成累加树,就是将每个节点的值加上树里面所有比它大的值;   那么遍历到第一个时,它是最大的,不需要加任何数,当遍历到后面的元素时,将前面累加的所有比它大的累加到这个元素内;   最后就得到了最后的累加树。    第506题: 出 N名运动员的成绩,找出他们的相对名次并授予前三名对应的奖牌。前三名运动员将会被分别授予“金牌”,“银牌”和“铜牌”("GoldMedal","SilverMedal","BronzeMedal")。来源:力扣(LeetCode) (注:分数越高的选手,排名越靠前。)     1、对数组内的各个成绩进行大小判断,前三可以获得金银铜牌;   我们需要先用哈希表记录下每个人成绩再原数组内的位置 ,然后对成绩进行排

  • spring-cloud-gateway(二)es代理功能需求

    实际是该项目的延申cclient/elasticsearch-multi-cluster-compat-proxy:网关代理兼容ES6es7proxyandcompatelasticsearchversion7andelasticsearchversion6's_searchand_bulkrequestapi(github.com) 项目用spring-boot-starter-webflux实现了es6和es7的兼容层网关,但只是做可行性验证 真正生产使用的网关还需要更多功能,最基本的,限流,熔断,负载均衡,这些都独立往项目里加,每个点都需要花精力整合 实际这方面应用已经有了很成熟的方案,服务治理相关,k8s,ingress,istio,kong,nginx... 但对es这类bare服务,套用云服务的方案并不合适,kong/nginx因为是c+lua的技术栈,定制的成本较高 nodejs已经过气了,go和java,考虑生态,选择java java方面的网关,很少单独提及,更多是做为java服务治理的一个组件来使用 java类网关,早些年大家都使用netty/mina原生实现 但后

  • BZOJ 1426: 收集邮票

    1426:收集邮票 TimeLimit: 1Sec  MemoryLimit: 162MBSubmit: 317  Solved: 253[Submit][Status][Discuss] Description 有n种不同的邮票,皮皮想收集所有种类的邮票。唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n。但是由于凡凡也很喜欢邮票,所以皮皮购买第k张邮票需要支付k元钱。现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。 Input 一行,一个数字NN<=10000 Output 要付出多少钱.保留二位小数 SampleInput 3 SampleOutput 21.25 HINT   Source   [Submit][Status][Discuss]   好水啊,太水了,不能再水了~~~~   先求出收集到i种邮票的期望购买次数,设$x$为买第i种时每次购买和之前

  • Windows下80端口被pid为4的System进程占用解决方法

    前言 之前是Windows7系统,前段时间装了Windows10,php环境还没来得及搭建。今天折腾了一下,是用nginx+php,端口是80(已经停止了iis服务),nginx就是起不来,十之八九就是端口被占用了。 发现 使用端口映射查看命令netstat-ano,发现80端口是被pid为4的System进程占用,按以前的经验:一是被IIS占用;二是SQLServerReportingServices报表服务占用。 注:如果是Windows7及以下的系统,解决上面两个基本上是没有问题的。 问题 上面所说两项服务均已停止,到底是什么程序占用了端口呢? 方法 使用HTTP命令netshhttpshowservicestate详细查看,会发现有80端口的信息,记下版本。 然后,往下找,找到与上面对应的版本 打开任务管理器,选择详细信息,找到对应的进程,然后右键转到服务 转到服务后,右键停止服务即可。 收工 一切就绪,可以安心码代码了。 本文来自博客园,作者:CodeHsu,转载请注明原文链接:https://www.cnblogs.com/seayxu

  • 跨域资源共享CORS

    浏览器的同源策略:协议相同and域名相同and端口相同。当一个http请求不满足以上的同源策略时就是跨域请求。比如: //协议不同 http://s.taobao.comhttps://s.taobao.com //端口不同 http://www.taobao.comhttp://www.taobao.com:8090 //域名不同 http://s.taobao.comhttp://www.taobao.com //js中的fetch方法会引起跨域请求 复制 注意:如果是通过href或者src请求下来的js文件、css文件、图片文件、视频文件都是不存在跨域请求的,只有通过AJAX请求请求数据时才会出现跨域问题。 解决浏览器跨域请求限制的方法: 方法一CORS: 这里只是简单的使用CORS //在服务端的响应头中允许跨域请求 response.header("Access-Control-Allow-Origin","*") 复制 方法二JSONP: 利用请求js文件文件不存在跨域请求的原理来实现的。 <!-- 客户端html文件 --> <html> <

  • 个人项目

    |这个作业属于哪个课程|网工194-软件工程| |----|----|----| |这个作业要求在哪里|完成个人项目并测试| |这个作业的目标|实现论文查重算法,学会使用PSP表格,学会Gitcommit规范,学会使用单元测试| 一、GitHub 个人项目 二、P2P表格 PSP2.1 PersonalSoftwareProcessStages 预估耗时(分钟) 实际耗时(分钟) Planning 计划 20 30 ·Estimate ·估计这个任务需要多少时间 1020 1145 Development 开发 400 510 ·Analysis ·需求分析(包括学习新技术) 30 30 ·DesignSpec ·生成设计文档 60 20 ·DesignReview ·设计复审 60 30 ·CodingStandard ·代码规范(为目前的开发制定合适的规范) 10 15 ·Design ·具体设计 30 30 ·Coding ·具体编码 280 340 ·CodeReview ·代码复审 20 30 ·Test ·测试(自我测试

  • LC 987. Vertical Order Traversal of a Binary Tree

    Givenabinarytree,returnthe verticalorder traversalofitsnodes values. Foreachnodeatposition (X,Y),itsleftandrightchildrenrespectively willbeatpositions (X-1,Y-1) and (X+1,Y-1). Runningaverticallinefrom X=-infinity to X=+infinity,whenevertheverticallinetouchessomenodes,wereportthevaluesofthenodesinorderfromtoptobottom(decreasing Y coordinates). Iftwonodeshavethesameposition,thenthevalueofthenodethatisreportedfirstisthevaluethatissmal

相关推荐

推荐阅读