海报生成失败

业务场景

二轮充电业务中,用户充电完成后在订单详情页展示订单相关信息,用户点击分享按钮唤起微信小程序分享菜单,将生成的图片海报分享给微信好友或者下载到本地,好友可通过扫描海报中的二维码加群领取优惠。

使用场景及功能:微信小程序 生成海报图片 分享好友 下载图片

使用技术:Taro vue vant canvas

实现效果图

重点步骤拆分

1、封装一个海报分享组件 poster-share.vue

2、用canvas画图,将背景图、费用、二维码等信息绘制在一张图上,其中费用、二维码是动态获取的

3、生成一张本地缓存图片

4、唤起微信分享功能,实现分享和下载功能

重点步骤有了,那么就开干吧!

核心代码实现

1、模版部分

需要一个画布dom用来绘制图片,一个用来存放生成图片的dom

问:canvasId为什么需要动态生成呢?

答:避免一个页面中使用多个组件引起的canvasId重复问题

<template>
  <div class="poster-share__content">
    <!-- canvas生成的海报图片 -->
    <img
      v-if="posterImg"
      class="poster-share__content--img"
      mode="aspectFit"
      :src="posterImg"
    >
    <!-- 分享海报canvas绘制部分 -->
    <canvas
      class="poster-share__content--cvs"
      :canvas-id="canvasId"
    ></canvas>
  </div>
</template>

2、样式部分

该业务场景下,不能让用户看到画布,但是设置canvas的display为none将不能进行绘制,会报如下错误,导致绘制失败。

实现方式:采用定位的方式,将canvas定位到可视区域外,具体代码如下。

.poster-share__content {
  position: absolute;
  right: -9999px;
  top: -9999px;
  width: 560px;
  height: 852px;
  opacity: 0;
  z-index: -1;

  &--img {
    width: 100%;
    height: 100%;
  }

  &--cvs {
    width: 100%;
    height: 100%;
  }
}



3、核心js部分

开始写核心实现啦~

父组件传参控制子组件是否开始绘制,子组件绘制完成后通知父组件改变状态。

  name: 'CpPosterShare',
  model: {
    prop: 'value',
    event: 'update:value',
  },
  props: {
    value: {
      type: Boolean,
      default: false,
    },
    config: {
      type: Object,
      default: () => ({}),
    },
  },
  data () {
    return {
      isDraw: false, // 是否开始绘制海报
      posterImg: '', // 生成的海报图片地址
      canvasId: `canvasId${ Math.random() }`,
      screenWidth: null, // 屏幕宽度
    }
  },
  watch: {
    value: {
      handler (val) {
        this.isDraw = val
      },
      immediate: true,
    },
    isDraw (val) {
      this.$emit('update:value', val)
      if (val) {
        this.init()
      }
    },
  },

首先,我们做的是一个小程序,将图片放在小程序源码中会加大包的体积,需要从网络上下载图片,因此需要封装一个公共的方法来获取图片的信息。Taro提供getImageInfo方法返回图片的原始宽高、本地路径等信息。

// 加载图片
loadImg (src) {
  return newPromise((resolve, reject) => {
    Taro.getImageInfo({
      src,
    }).then((res) => {
      resolve({ ...res })
    }).catch((err) => {
      reject(err)
    })
  })
}



该业务场景中涉及绘制多张图片,包括背景图片和二维码图片,需要将多张图片都load完成后才能开始绘制。

const promiseParams = [this.loadImg(BgImage), this.loadImg(QRcode)]
const promiseAll = Promise.all(promiseParams.map((item) =>item.catch(() =>null)))

promiseAll.then((res) => {
  this.draw(res)
}).catch((err) => {
  console.log(err)
})

开始绘制啦~

创建canvas绘图上下文CanvasContext对象,调用Taro提供的方法Taro.createCanvasContext(canvasId)绘制背景图、绘制价格、绘制二维码,这里就不一一赘述了。全部绘制完成后,将画布中的内容导出生成图片,Taro提供了canvasToTempFilePath方法,需要在draw()回调中调用才能保证图片导出成功,返回生成图片的临时路径。

ctx.draw(false, () => {
  Taro.canvasToTempFilePath({
    canvasId:this.canvasId,
  }).then((res) => {
    this.posterImg = res.tempFilePath

    // 唤起分享菜单
    this.showShareImageMenu()
  }).catch((err) => {
    console.log('海报生成失败', err)
    Taro.showToast({
      title: '海报生成失败',
      icon: 'error',
    })
  }).finally(() => {
    Taro.hideLoading()
    this.isDraw = false
  })
})

本地图片生成成功后,唤起微信提供的分享菜单弹窗,可以将图片发送给朋友、收藏、保存到相册。Taro提供showShareImageMenu方法唤起分享菜单弹窗,入参为本地图片路径。

showShareImageMenu () {
  if (Taro.showShareImageMenu) {
    Taro.showShareImageMenu({
      path:this.posterImg,
    }).then().catch((err) => {
      console.log(err)
      const { errMsg } = err
      // 取消操作  errMsg === 'showShareImageMenu:fail cancel'
      // 拒绝授权  errMsg: "showShareImageMenu:fail auth deny"
      if (errMsg === 'showShareImageMenu:fail auth deny') {
        authorize({
          scope:'writePhotosAlbum',
          showModal:true,
          authName:'保存图片到相册',
          success: () => {
            this.downloadImg()
          },
        })
      }
    }).finally(() => {
      this.isDraw = false
    })
  } else {
    Taro.showToast({
      title:'小程序版本不支持该功能',
      icon:'error',
    })
  }
}



用户点击发送给朋友,会调起微信对话框,将生成的海报图片粘贴分享给朋友;

点击收藏,会将海报图片添加到收藏列表中,方便下次查看;

点击保存到相册,会唤起保存图片授权弹窗,用户点击允许,会将海报图片保存在本地相册中。

如果用户在保存图片授权弹窗中第一次点击拒绝,之后再次点击分享下载时,需要有授权提示弹窗,提示用户是否打开设置去授权,具体展示如下。

Taro提供Taro.openSetting方法调起小程序设置页面,用户开启“添加到相册”授权后成功后,调用Taro提供的下载图片的方法Taro.saveImageToPhotosAlbum将图片下载到本地。

授权提示弹窗 设置页面 下载提示

其中判断用户是否开启授权的方法具体实现如下:

/**
 * 权限获取流程
 * @param scope       权限英文名称
 * @param success     授权成功的回调
 * @param fail        授权失败的回调
 * @param showModal   授权失败是否展示对话框提示
 * @param authName    授权失败是否展示对话框提示展示的授权名称
 *  // 例子:开启用户的相册权限
    authorize({
      scope: 'writePhotosAlbum',
      showModal: true,
      authName: '保存图片到相册',
      success () {
        console.log('授权成功')
      },
    })
*/
export async function authorize (options) {
  const {
    scope, success, fail, showModal = false, authName = '',
  } = options
  try {
    const scopeName = `scope.${ scope }`
    const auth = await Taro.getSetting()
    if (!auth.authSetting[scopeName]) {
      Taro.authorize({ scope: scopeName }).then((res) => {
        if (res.errMsg === 'authorize:ok' && success) success()
      }, () => {
        if (showModal && authName) {
          Taro.showModal({
            title: '授权提示',
            content: `您拒绝了${ authName }权限,是否打开设置去授权?`,
          }).then((res) => {
            if (res.confirm) {
              Taro.openSetting().then((res2) => {
                if (res2.authSetting[scopeName] && success) {
                  success()
                } else if (fail) {
                  fail()
                }
              })
            } else {
              fail && fail()
            }
          }).catch((res) => {
            fail && fail(res)
          })
        } else {
          fail && fail()
        }
      })
    } else {
      success && success()
    }
  } catch (err) {
    fail && fail(err)
  }
}

至此,具体实现完结撒花~ 可以将组件用到页面中了

组件引用

<poster-share
  v-model="draw"  // 是否开始绘制海报海报
  config="config"  // 海报配置信息
/>



问题记录

在开发过程中遇到了一些问题,记录一下

现象:点击分享,生成canvas图片。开发者工具上每次都正常,ios机每次都正常,部分安卓机每次都正常,部分安卓机,点击分享之后取消,操作多次,有几次会生成图片失败

报错信息:"errMsg": "canvasToTempFilePath:fail :create bitmap failed"

错误定位解决:canvas需要一直显示,不能有display:none的情况

作者:京东零售 张梦雨

内容来源:京东云开发者社区

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

相关文章

  • NCL专辑 | 常用插值函数集锦

    NCL作为一门气象专业语言,自带了很多气象届常用的算法和命令,比如各种强大的插值函数。平时,我们不管做科研也好,还是做业务也好,都逃不了各种各样的插值:站点插到格点上,格点插到站点上,高分辨率插值到低分辨率格点,低分辨率插值到高分辨率,还有各种模式输出产品往站点、格点上插……NCL没出来之前,我用fortran写插值经常写地焦头烂额(不好!暴露年龄了!)后来有了NCL,导师再也不怕你因为插值而卡在那里一个月没有任何进展啦!(然而NCL停更了耶。。)好啦,现在让我们一起快乐地插值吧!NCL的插值函数都在ngmath库(该库是Fortran、C语言、NCL可直接调用的数学命令的集合)中。根据插值方法的不同,NCL的插值函数主要可以分为以下几类:csagrid系列:该系列函数利用一个三次样条近似演算法来拟合输入数据的函数。函数的输入值是一组随机间隔的数据,这些数据可以是一维、二维或三维的。注意,csagrid是ngmath数据库中唯一一个为三维数据提供拟合曲面近似的软件包。计算插值和近似方法可以分为两个基本类:拟合函数方法和加权平均数方法。拟合函数方法是对已知数据拟合一个代数曲面,然后从拟合

  • Jmeter系列(28)- 发送 soap 协议的接口

    SOAP协议介绍SimpleObjectAccessProtocol,简单对象访问协议一种轻量的、简单的、基于XML的协议SOAP跟HTTP、SMTP等一样是一种传输协议WebService三要素:SOAP、WSDL、UDDI使用Jmeter做soap协议接口的测试免费soap协议接口的地址可以在这网站找到各式soap协议的接口进行测试:http://www.webxml.com.cn/zh_cn/weather_icon.aspx测试计划结构树soapv1.1版本的栗子接口文档HTTP请求请求头测试结果soapv1.2版本的栗子接口文档HTTP请求请求头测试结果总结测试soap协议的接口时,用HTTPSampler来发出请求就行了HTTP请求头按接口文档给的写就好了,一般都会有,因为请求数据就是XML格式的Content-type:application/xmlXML格式的请求数据填在消息体数据一栏中

  • 解锁新姿势:探讨复杂的 if-else 语句“优雅处理”的思路

    点击上方"IT牧场",选择"设为星标"技术干货每日送达!来源:hyzhan43juejin.im/post/5def654f51882512302daeef前言需求编码实现思考策略模式什么是策略模式?编码深思工厂+策略toMap效果后续最后前言在之前文章说到,简单if-else,可以使用卫语句进行优化。但是在实际开发中,往往不是简单if-else结构,我们通常会不经意间写下如下代码:--------------------理想中的if-else-------------------- publicvoidtoday(){ if(isWeekend()){ System.out.println("玩游戏"); }else{ System.out.println("上班!"); } } --------------------现实中的if-else-------------------- if(money>=1000){ if(type==UserType.SILVER_VIP.getCode())

  • 通俗易懂讲一下:QOS 概念及术语

    session1QOS概念及术语澄清TOS、DSCP、PHB、COS、EXP(MPLS的)、ipv6TC(ipv6TrafficClass)ipv4的包头中TOS字段定义了QOS服务质量,其中的被IEEE在RFC中规定的对应关系是:xxx这个是ippredence=cos=Exp xxxxxx这个是dscp=ipv6TC(8bit用前6bit) xxxxxxxx这个是TOS字段,前3bit对应ipp=cos=exp,前6bit对应dscp=ipv6TC,后面2个xx保留 xxxdtrc?TOS字段,前3bit表示优先级,后面5bit中分别是D=时延,T=带宽,R=可靠性,C=cost开销(保留),?=保留 在PHB中前3bit表示优先级,后2个bit表示丢包,第6bit固定=0,最后2bit保留 复制综上所述,TOS中的bit包含了ipp、cos、exp、dscp、ipv6tc、phb所用到的bit1、DSCP与PHBDSCP只是个标记,PHB是每一跳执行动作。就比如DSCP有0-63=64个,但是这64个之中只有16个值被规定了有相应的PBH转发动作。这个是IEEE规定的在多

  • 人工智能胶囊系统以最先进的精度区分物体

    编辑|TGS 发布|ATYUN订阅号强有力的证据表明,人类总是依靠坐标系或参考线和曲线来推测空间中点的位置。这与广泛使用的计算机视觉算法不同,后者往往通过物体特征的数字表示来区分物体。为了寻求一种让机器更像人类的方法,谷歌、Alphabet子公司DeepMind和牛津大学的研究人员提出了堆叠式胶囊自动编码器(SCAE)——它利用物体各部分之间的几何关系来推理物体。由于这些关系不依赖于模型查看对象的位置,所以即使在视图发生变化时,模型也能高精度地对目标进行分类。2017年,人工智能领域最重要的理论家和图灵奖获得者——杰弗里·辛顿,与学生萨拉·萨布尔和尼古拉斯·弗罗斯特一起提出了名为“CapsNet”的机器学习体系架构,这是一种经过差别训练的多层次方法,在流行的基准上实现了最先进的图像分类性能。今年早些时候,辛顿、萨伯和牛津机器人研究所的研究人员详细介绍了SCAE,它在一些关键方面改进了原有的架构。SCAE和其他胶囊系统通过几何解释有组织的部件集合,进而理解物体。具体来说就是,负责分析各种对象属性(如位置、大小和色调)的数学函数集胶囊被添加到一种经常用于分析视觉图像的人工智能模型上,并且多

  • Facebook系统HTML转PDF文档可能引起的RCE漏洞

    该漏洞能让攻击者在Facebook的tapprd.legal.thefacebook.com服务端(Server-Side)执行HTML代码,如此实现远程代码执行(RCE)。原因在于漏洞页面中用于填充输入的HTML标签未经转义,就被直接传递给了“HTML至PDF转化器”(HTMLtoPDFConverter)进行下一步文件转化。以下为作者的分享思路。HTML转PDF过程中存在的漏洞1、WorkplacebyFacebook为Facebook旗下办公通讯软件,通过公司或群组模式实现内部团队交流沟通。当属于公司或群组的个人创建WorkplacebyFacebook账号时,会从Facebook官方邮箱legal_noreply@fb.com收到一封确认邮件,该邮件中包含一个需由帐号所有者签署的在线协议URL,而该URL中包含一个特殊的token,如下:https://legal.tapprd.thefacebook.com/tapprd/Portal/ShowWorkFlow/AnonymousShowStage?token=打开以上URL页面后,其中包括需由用户输入的姓名、地址、电邮、职业

  • 使用django-allauth管理用户登录与注册

     django-allauth是非常受欢迎的管理用户登录与注册的第三方Django安装包,django-allauth集成了local用户系统和social用户系统,其social用户系统可以挂载多个账户。 django-allauth能实现以下核心功能:用户注册用户登录退出登录第三方auth登录(微信,微博等)邮箱验证修改邮箱修改密码忘记密码,登录后邮箱发送密码重置链接安装与配置安装django-allauthallenlideMacBook-Pro:~allen$mkvirtualenvoauth (oauth)allenlideMacBook-Pro:~allen$pipinstalldjango (oauth)allenlideMacBook-Pro:~allen$pipinstalldjango-allauth复制创建Django项目项目基础配置安装好后设置oauth/settings.py,将allauth相关APP加入到INSTALLED_APP里去。对于第三方的providers,你希望用谁就把它加进去。值得注意的是allauth对于站点设置django.contrib

  • Spring Boot 这样学才对!

    在过去两三年的Spring生态圈,最让人兴奋的莫过于SpringBoot框架。SpringBoot应用本质上就是一个基于Spring框架的应用,它是Spring对“约定优先于配置”理念的最佳实践产物,它能够帮助开发者更快速高效地构建基于Spring生态圈的应用。那SpringBoot有何魔法?自动配置、起步依赖、Actuator、命令行界面(CLI)是SpringBoot最重要的4大核心特性,本文将为你打开SpringBoot的大门,重点为你剖析其启动流程以及自动配置实现原理。一、抛砖引玉:探索SpringIoC容器如果有看过SpringApplication.run()方法的源码,SpringBoot冗长无比的启动流程一定会让你抓狂,透过现象看本质,SpringApplication只是将一个典型的Spring应用的启动流程进行了扩展,因此,透彻理解Spring容器是打开SpringBoot大门的一把钥匙。1.1、SpringIoC容器可以把SpringIoC容器比作一间餐馆,当你来到餐馆,通常会直接招呼服务员:点菜!可能你根本不关心菜的原料是什么。IoC容器也是一样,你只需要告诉它

  • RStudio发布新接口,在R语言中使用TensorFlow

    R语言是一种自由软件编程语言与操作环境,主要用于统计分析、绘图、数据挖掘、机器学习等。今日RStudio发布博文称,已为TensorFlow创建了R接口,使R用户能方便的使用TensorFlow。在过去的一年中,我们一直在努力为Google的开源机器学习框架TensorFlow创建R接口。我们之所以如此关注它,最重要的是TensorFlow为深度学习应用提供了最先进的基础设施。在谷歌开源后的这两年里,TensorFlow迅速成为机器学习从业者和研究人员的首选框架。周六,我们的JJAllaire在rstudio::conf的主题演讲中正式宣布了我们关于TensorFlow的工作:视频链接:http://imgcdn.atyun.com/2018/02/Machine-Learning-with-R-and-TensorFlow.mp4?_=1在主题演讲中,JJ不仅描述了我们在TensorFlow上所做的工作,而且还深入地讨论了深度学习(深度学习是什么,它是如何工作的,以及它在未来几年可能与R的用户相关的地方,视频搬运自youtube)。新的包和工具TensorFlow的R接口由一套R包组

  • c#-二分查找-算法

    折半搜索,也称二分查找算法、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。 A搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束; B如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。 C如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。 时间复杂度折半搜索每次把搜索区域减少一半,时间复杂度为。(n代表集合中元素的个数)空间复杂度    ///<summary> ///二分查找 ///</summary> ///<paramname="arr"></param> ///<paramname="low">开始索引0</param> ///<paramname="high">结束索引</param> ///<paramname="key">要查找的对象</param> ///<returns></ret

  • 链表应用题

    1.递归删除指定值(无头结点) voidDel(ListNode*L,intval){ ListNode*p;//指向被删除节点 if(L==NULL)return;//递归边界 if(L->val==val){//处理首指针 p=L; L=L->next; free(p);//删除节点 Del(L,val);//递归调用 } elseDel(L->next,val);//递归调用 } 复制 2.循环删除指定值(有头结点) voidDel(ListNode*L,intval){ ListNode*p=L->next;//遍历节点 ListNode*pre=L;//辅助删除节点 ListNode*q;//删除节点 while(p!=NULL){//遍历节点存在 if(p->val==val){//找到指定值,更改指针结构 q=p; p=p->next; pre->next=p; free(q); } else{ pre=p; p=p->next; } } } 复制 3.反向输出每个值(有头结点) //主函数从头结点下一个节点调用函数 voi

  • isula-build编译记录

    isula-build官网:https://gitee.com/openeuler/isula-build 第一遍按照其文档编译时没成功,此处记录一下。 编译环境,直接在华为云申请一台:openEuler20.0364bitwithARM 使用root用户登录。 1.安装依赖包 yuminstallmakebtrfs-progs-develdevice-mapper-develglib2-develgpgme-devellibassuan-devellibseccomp-develgitbzip2systemd-develgolang 复制 注意isula-build对go的版本是有要求的,需要1.15及以上,一开始没注意导致失败了。openeuler的源没这么新,这里手动安装一下。 2.安装新版本go wgethttps://go.dev/dl/go1.17.6.linux-arm64.tar.gz sha256sumgo1.17.6.linux-arm64.tar.gz tar-C/usr/local-xzfgo1.17.6.linux-arm64.tar.gz echo"exp

  • 小程序开发全栈1.2/3/4组件、flex布局、样式

    1.2组件 1.2.1text组件 编写文本信息,类似于HTTP中的span 1.2.2view组件 容器,类似于HTTP中的div 1.2.3image组件 图片显示组件 1.3页面flex布局 一种非常方便的通用布局方式 1.3.1flex-direction 规定主轴方向 column:主轴竖直 row:主轴水平 1.3.2justify-content 规定主轴方向上的排列方式 flex-start/end center space-around space-between 1.3.3align-items 规定副轴方向排列方式 flex-start/end center space-around space-between 1.3.4示例: display:flex; flex布局 flex-direction:row; 规定主轴方向:row/column justify-content:space-around; 元素在主轴方向上的排列方式:flex-start/end/space-around/space-between

  • 分红包算法Java实现

    需要考虑几个点:   红包形成的队列不应该是从小到大或者从大到小,需要有大小的随机性。     红包这种金钱类的需要用Decimal保证精确度。     考虑红包分到每个人手上的最小的最大的情况。   下面是利用线段分割算法实现的分红包,比如把100元红包,分给十个人,就相当于把(0-100)这个线段随机分成十段,也就是再去中找出9个随机点。 找随机点的时候要考虑碰撞问题,如果碰撞了就重新随机(当前我用的是这个方法)。这个方法也更方便抑制红包金额MAX情况,如果金额index-start>MAX,就直接把红包设为最大值MAX, 然后随机点重置为start+MAX,保证所有红包金额相加等于总金额。 importjava.math.BigDecimal; importjava.util.*; publicclassRedPaclage{ publicstaticList<Integer>divideRedPackage(intallMoney,intpeopleCount,int

  • 1、saltstack 安装部署

    一、介绍和安装 saltstack和ansible、puppet都是自动化运维工具。 1、saltstack的几种使用方式 local minion/master(C/S) syndic(代理模式) SSH 2、架构 主机 角色 hadoop1 master、minion hadoop2 minion hadoop3 minion 系统版本:Centos7 二、安装 官网地址 https://repo.saltproject.io/#rhel 1、配置yum源 sudorpm--importhttps://repo.saltproject.io/py3/redhat/7/x86_64/latest/SALTSTACK-GPG-KEY.pub curl-fsSLhttps://repo.saltproject.io/py3/redhat/7/x86_64/latest.repo|sudotee/etc/yum.repos.d/salt.repo 复制 2、节点安装master和minion hadoop1:sudoyuminstallsalt-mast

  • 评论列表显示及排序,个人中心显示

    1.评论 {%extends"myweb.html"%} {%blockdetailtitle%}问答详情{%endblock%}   {%blockdetailhead%} {%endblock%}   {%blockmywebbody%}       <div class="page-header"style="color:black"align="center">         <h3>{{ques.title}}<br><small>{{ques.author.username}}<span class="badge">{{ques.creat_time}}</span></small></h3>         </div>  &n

  • 树状数组

    树状数组 基本概念 假设数组a[1..n],那么查询a[1]+...+a[n]的时间是log级别的,而且是一个在线的数据结构,支持随时修改某个元素的值,复杂度也为log级别。 树状数组的结构图 令这棵树的结点编号为C1,C2...Cn。令每个结点的值为这棵树的值的总和,那么容易发现: C1=A1 C2=A1+A2 C3=A3 C4=A1+A2+A3+A4 C5=A5 C6=A5+A6 C7=A7 C8=A1+A2+A3+A4+A5+A6+A7+A8 ... C16=A1+A2+A3+A4+A5+A6+A7+A8+A9+A10+A11+A12+A13+A14+A15+A16 这里有一个有趣的性质: 设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax, 所以很明显:Cn=A(n–2^k+1)+...+An 算这个2^k有一个快捷的办法,定义一个函数如下即可: 1 2 3 int lowbit(int x){ return x&(x^(x–1)); }

  • 【SWUST OJ】 698: Independent Task Scheduling

    IndependentTaskScheduling 首先给予dp数组定义 dp[i][j]为前i个工作,A工作时间为j时,B的工作时间 然后这题就变成水题了 首先状态转移方程为  dp[i][j]=min(dp[i-1][j-a[i]],dp[i-1][j]+b[i]); 然后枚举每一个A所需要的时间直到A机器所有作业时间总和 取得max(i,dp[n][i])为做完所有作业需要的时间,然后取最小值即为所求   #include<iostream> #include<cstdio> #include<string> #include<stack> #include<queue> #include<map> #include<set> #include<vector> #include<cmath> #include<bitset> #include<algorithm> #include<climits> #includ

  • twemproxy发送流程探索——剖析twemproxy代码正编

    本文想要完成对twemproxy发送流程——msg_send的探索,对于twemproxy发送流程的数据结构已经在《twemproxy接收流程探索——剖析twemproxy代码正编》介绍过了,msg_send和msg_recv的流程大致类似。请在阅读代码时,查看注释,英文注释是作者对它的代码的注解,中文注释是我自己的感悟。 函数msg_send 1rstatus_t 2msg_send(structcontext*ctx,structconn*conn) 3{ 4rstatus_tstatus; 5structmsg*msg; 6/*表示活跃的发送状态*/ 7ASSERT(conn->send_active); 8/*表示准备发送*/ 9conn->send_ready=1; 10do{ 11/*获取下一次发送的msg开头*/ 12msg=conn->send_next(ctx,conn); 13if(msg==NULL){ 14/*nothingtosend*/ 15returnNC_OK; 16} 17/*发送框架,在此框架内conn->send_read

  • 攻防世界web_php_unserialize题解

    攻防世界web_php_unserialize题解 前置知识 正则表达式 /[oc]就是正则表达式的意思 \d:匹配一个数字字符。等价于[0-9]。 +:匹配前面的子表达式一次或多次。例如,'zo+'能匹配"zo"以及"zoo",但不能匹配"z"。+等价于{1,}。 /i:表示匹配的时候不区分大小写 复制 preg_match函数 参考:https://www.runoob.com/php/php-preg_match.html preg_match函数用于执行一个正则表达式匹配。 intpreg_match(string$pattern,string$subject[,array&$matches[,int$flags=0[,int$offset=0]]]) 复制 $pattern:要搜索的模式,字符串形式。 $subject:输入字符串。 $matches:如果提供了参数matches,它将被填充为搜索结果。$matches[0]将包含完整模式匹配到的文本,$matches[1]将包含第一个捕获子组匹配到的文本,以此类推。 $flags:flags可以被设置为以下标记值:

  • 信号总结(一)

    信号 对信号的三种操作:忽略捕获 默认 void*Signal(intsigno,(void(*func)(int)))(int) 不可靠信号:信号已发生,但进程不知道。 多个信号的处理问题 在某个信号处理期间来了此种信号 在某个信号处理期间来了另一种信号 信号对系统调用的影响:1).设置errno返回;2)重启调用。 可重入函数:被信号中断后重启调用。 不是可重入条件:1).使用静态数据 2).调用malloc,free类函数 3).标准I/O函数。 SIGCHD  SIGCHLD raise(pid_tpid,intsigno) kill(intsigno) 设置signon=0,kill()用来检测某个特定进程是否仍旧存在。但ID会被重复利用,现有的ID可能并不是你想要的。这种测试不是原子操作。 unsignedintalrm(unsignedintsecond); intpause(void); 使用alrm(),pause()信号实现sleep()功能。 讲述了使用信号的各种注意事项。 1).使用arlm()时,若前面设置了alrm().则会刷新前面

相关推荐

推荐阅读