uniapp-原生地图截屏返回base64-进行画板编辑功能

一、场景

vue写uniapp打包安卓包,实现原生地图截屏(andirod同事做的)-画板编辑功能

实现效果:

二、逻辑步骤简略

1. 由 原生地图nvue部分,回调返回 地图截屏生成的base64 数据,

2. 通过 uni插件市场 image-tools 插件 base64ToPath方法,将base64数据 转成文件路径

3. 通过 uni -API- uni.createCanvasContext() 、ctx.createPattern() 方法,将 图片数据 创建绘图对象

4. 通过 uni - movable-area+movable-view 控制画布缩放

5. 通过 canvas @touchmove="touchmove"  @touchend="touchend"  @touchstart="touchstart" 等方法实现在画布上绘制画笔

6. 生成图片及清空画布

三、具体实现

1.  由 原生地图nvue部分,回调返回 原生地图截屏生成的base64 数据(andirod同事做的)

2.  image-tools 插件 base64ToPath 

image-tools - DCloud 插件市场

import { pathToBase64, base64ToPath } from '@/js_sdk/mmmm-image-tools/index.js'

3.通过 uni -API- uni.createCanvasContext() 、ctx.createPattern() 方法

uni-app官网 API- createPattern()

initC() {
      const that = this
      // 创建绘图对象
      this.ctx = uni.createCanvasContext('mycanvas', this);
      // 在canvas设置背景 - 入参 仅支持包内路径和临时路径
      const pattern = this.ctx.createPattern(this.imageUrl, 'repeat-x')
      this.ctx.fillStyle = pattern
      this.ctx.setStrokeStyle('red')
      this.ctx.fillRect(0, 0, this.dWidth, this.dHeight)
      this.ctx.draw()
      // 方法二  在画布上插入图片
      // this.img = new Image();
      // this.img.src = this.imageUrl;
      // this.img.onload = () => {
      //   console.log('this.img', that.img.width)
      //   that.ctx.drawImage(that.img, 0, 0, this.dWidth, this.dHeight)
      //   // that.ctx.draw()
      // }
    },

4. 通过 uni - movable-area+movable-view 控制画布缩放

<movable-area :scale-area="true" :style="{'width':windowWidth+'px','height':windowHeight+'px','backgroundColor':'#ddd','overflow':'hidden'}">
      <movable-view 
        direction="all"
        :inertia="false"
        :out-of-bounds="false"
        :scale-min="0.001"
        :scale-max="4"   
        :scale="true"
        :disabled="movableDisabled"
        :scale-value="scaleValue"
        class="pr"
        :style="{'width':widths+'px','height':heights+'px'}"
        @scale="scaleChange">
          <canvas
            id="mycanvas"
            canvas-id="mycanvas"
            :style="{'width':widths+'px','height':heights+'px'}"
            @touchmove="touchmove"
            @touchend="touchend"
            @touchstart="touchstart">
          </canvas>
      </movable-view>
    </movable-area>

5.通过 canvas @touchmove="touchmove"  等方法实现在画布上绘制画笔

touchstart(e) {
      let startX = e.changedTouches[0].x
      let startY = e.changedTouches[0].y
      if (this.scaleValue > 1) {
        startX = e.changedTouches[0].x / this.scaleValue;
        startY = e.changedTouches[0].y / this.scaleValue;
      } else {
        startX = e.changedTouches[0].x * this.scaleValue;
        startY = e.changedTouches[0].y * this.scaleValue;
      }
      console.log('touchstart()-x', e.changedTouches[0].x, 'scaleValue', this.scaleValue, 'startX', startX)
      let startPoint = { X: startX, Y: startY };
      this.points.push(startPoint);
      // 每次触摸开始,开启新的路径
      this.ctx.beginPath();
	  },
    touchmove(e) {
      if (this.isEdit) {
        let moveX = e.changedTouches[0].x
        let moveY = e.changedTouches[0].y
        if (this.scaleValue > 1) {
          moveX = e.changedTouches[0].x / this.scaleValue;
          moveY = e.changedTouches[0].y / this.scaleValue;
        } else {
          moveX = e.changedTouches[0].x * this.scaleValue;
          moveY = e.changedTouches[0].y * this.scaleValue;
        }
        console.log('touchmove()-x', e.changedTouches[0].x, 'scaleValue', this.scaleValue, 'moveX', moveX)
        let movePoint = { X: moveX, Y: moveY };
        this.points.push(movePoint); // 存点
        let len = this.points.length;
        if (len >= 2) {
          this.draw(); // 绘制路径
        }
      }
	  },
    touchend() {
      this.points = [];
	  },
    draw() {
      let point1 = this.points[0];
      let point2 = this.points[1];
      this.points.shift();
      this.ctx.moveTo(point1.X, point1.Y);
      this.ctx.lineTo(point2.X, point2.Y);
      this.ctx.stroke();
      this.ctx.draw(true);
    },

6.生成图片及清空画布

clear() {
      let that = this;
      this.scaleValue = 1
      this.isEdit = false
      this.movableDisabled = false
      uni.getSystemInfo({
        success: function(res) {
          let canvasw = res.windowWidth;
          let canvash = res.windowHeight;
          that.ctx.clearRect(0, 0, canvasw, canvash);
          const pattern = that.ctx.createPattern(that.imageUrl, 'repeat-x')
          that.ctx.fillStyle = pattern
          that.dWidth = 285
          that.dHeight = 200
          that.ctx.setStrokeStyle('red')
          that.ctx.fillRect(0, 0, that.dWidth, that.dHeight)
          that.ctx.draw()
          // that.ctx.draw(true);
        }
      });
    },
    finish() {
      let that = this;
      uni.canvasToTempFilePath({
          canvasId: 'mycanvas',
          success: function(res) {
            // 这里的res.tempFilePath就是生成的签字图片
            // console.log('tempFilePath', res.tempFilePath);
            that.tempFilePath = res.tempFilePath
            that.$emit('onImgUrl', that.tempFilePath) // 向父级组件传值
          }
      });
    },

utils:

// 是否是 base64数据
export function isBase64Two(str) {
	try {
		return btoa(atob(str)) === str;
	} catch (err) {
		return false;
	}
}
export function isBase64(str) {
	// 正则表达式匹配B4-64编码格式
	const regex = /^[a-zA-Z0-9+\/]+={0,2}$/;
	return regex.test(str);
}
// 校验内容是否包含base64格式的图片
export function isBase64Three(str){
  let imgReg = RegExp(/data:image\/.*;base64,/)
  const res = imgReg.test(str)
  return res
}

四、总结

以下完整代码 DrawingBoard.vue:

<template>
  <view class="canvas-frame">
    <view class="icon-frame">
      <uni-icons 
        :class="{ 'is-edit': isEdit }" 
        type="compose" 
        size="18" class="icon-item mr10" 
        @click="createCanvas">编辑
      </uni-icons>
      <uni-icons
        type="plus"
        size="18" class="icon-item mr10"
        title="放大"
        @click="plusImageScalex">
      </uni-icons>
      <uni-icons
        type="minus"
        size="18" class="icon-item"
        title="缩小"
        @click="minusImageScalex">
      </uni-icons>
    </view>
    <view class="button-frame">
      <button size="mini" class="mr10" @click="clear">清空</button>
      <button size="mini" @click="finish">确定</button>
    </view>
    <!-- style="border: 1rpx solid #ccc;width: 570rpx; height: 400rpx;" -->
    <!-- <canvas
      id="mycanvas"
      canvas-id="mycanvas"
      :style="{'width':widths+'px','height':heights+'px'}"
      @touchmove="touchmove"
      @touchend="touchend"
      @touchstart="touchstart">
    </canvas> -->
    <movable-area :scale-area="true" :style="{'width':windowWidth+'px','height':windowHeight+'px','backgroundColor':'#ddd','overflow':'hidden'}">
      <movable-view 
        direction="all"
        :inertia="false"
        :out-of-bounds="false"
        :scale-min="0.001"
        :scale-max="4"   
        :scale="true"
        :disabled="movableDisabled"
        :scale-value="scaleValue"
        class="pr"
        :style="{'width':widths+'px','height':heights+'px'}"
        @scale="scaleChange">
          <canvas
            id="mycanvas"
            canvas-id="mycanvas"
            :style="{'width':widths+'px','height':heights+'px'}"
            @touchmove="touchmove"
            @touchend="touchend"
            @touchstart="touchstart">
          </canvas>
      </movable-view>
    </movable-area>
  </view>
</template>

<script>
// import { fabric } from 'fabric';
// import { fabric } from '@/utils/fabric.min.js';
// import { Database64ToFile } from '@/utils/index';
import { pathToBase64, base64ToPath } from '@/js_sdk/mmmm-image-tools/index.js'
import { isBase64 } from '@/utils/index.js';
// isBase64 方法判断 原生端返回到的数据格式是否正确
export default {
  props: {
    // 更新 原始地图画布
    mapImageUrl: {
      type: String,
      default: '',
    }
  },
  data() {
    return {
      canvasEle: null,
      isEdit: false,
      imageContainer: null,
      scaleValue: 1,
      ctx: '', // 绘图图像
      points: [], // 路径点集合
      tempFilePath: '', // 签名图片
      imageUrl: require('@/static/res/imgs/all/fushanhou-area.jpg'), // 本地图片画布资源
      img: null,
      dWidth: 285,
      dHeight: 200,
      widths: 285,
      heights: 200,
      windowWidth: 285,
      windowHeight: 200,
      movableDisabled: false,
    };
  },
  mounted() {
    this.initC()
  },
  watch: {
    mapImageUrl(newV, oldV) {
      const that = this
      console.log('watch()-mapImageUrl-newV,监听数据变化-newV', newV? '有值': '无值')
			if (!['',undefined,null].includes(newV)) {
        console.log('watch()-mapImageUrl-isBase64(newV)', isBase64(newV))
        // const base64Image = 'data:image/png;base64,/9j/4AAQSkZJRgA...'; 
        // that.base64ToTempFilePath(newV ,(tempFilePath) => {
        //   console.log('转换成功,临时地址为:', tempFilePath)
        //   that.imageUrl = tempFilePath 
        //   // 会在canvas中调用
        //   that.initC()
        // }, 
        // () =>{
        //   console.log('fail转换失败')
        // });
        const base64 = 'data:image/png;base64,' + newV;
        base64ToPath(base64).then((tempFilePath) => {
          console.log('转换成功,临时地址为:', tempFilePath)
          that.imageUrl = tempFilePath
          that.initC()
        })
			}
		},
  },
  methods: {
    
    initC() {
      const that = this
      // 创建绘图对象
      this.ctx = uni.createCanvasContext('mycanvas', this);
      // 在canvas设置背景 - 入参 仅支持包内路径和临时路径
      const pattern = this.ctx.createPattern(this.imageUrl, 'repeat-x')
      this.ctx.fillStyle = pattern
      this.ctx.setStrokeStyle('red')
      this.ctx.fillRect(0, 0, this.dWidth, this.dHeight)
      this.ctx.draw()
      // 方法二  在画布上插入图片
      // this.img = new Image();
      // this.img.src = this.imageUrl;
      // this.img.onload = () => {
      //   console.log('this.img', that.img.width)
      //   that.ctx.drawImage(that.img, 0, 0, this.dWidth, this.dHeight)
      //   // that.ctx.draw()
      // }
    },
    createCanvas() {
      this.isEdit = !this.isEdit
      if (this.isEdit) {
        this.movableDisabled = true
        // 设置画笔样式
        this.ctx.lineWidth = 2;
        this.ctx.lineCap = 'round';
        this.ctx.lineJoin = 'round';
      } else {
        this.movableDisabled = false
      }
    },
    touchstart(e) {
      let startX = e.changedTouches[0].x
      let startY = e.changedTouches[0].y
      if (this.scaleValue > 1) {
        startX = e.changedTouches[0].x / this.scaleValue;
        startY = e.changedTouches[0].y / this.scaleValue;
      } else {
        startX = e.changedTouches[0].x * this.scaleValue;
        startY = e.changedTouches[0].y * this.scaleValue;
      }
      console.log('touchstart()-x', e.changedTouches[0].x, 'scaleValue', this.scaleValue, 'startX', startX)
      let startPoint = { X: startX, Y: startY };
      this.points.push(startPoint);
      // 每次触摸开始,开启新的路径
      this.ctx.beginPath();
	  },
    touchmove(e) {
      if (this.isEdit) {
        let moveX = e.changedTouches[0].x
        let moveY = e.changedTouches[0].y
        if (this.scaleValue > 1) {
          moveX = e.changedTouches[0].x / this.scaleValue;
          moveY = e.changedTouches[0].y / this.scaleValue;
        } else {
          moveX = e.changedTouches[0].x * this.scaleValue;
          moveY = e.changedTouches[0].y * this.scaleValue;
        }
        console.log('touchmove()-x', e.changedTouches[0].x, 'scaleValue', this.scaleValue, 'moveX', moveX)
        let movePoint = { X: moveX, Y: moveY };
        this.points.push(movePoint); // 存点
        let len = this.points.length;
        if (len >= 2) {
          this.draw(); // 绘制路径
        }
      }
	  },
    touchend() {
      this.points = [];
	  },
    draw() {
      let point1 = this.points[0];
      let point2 = this.points[1];
      this.points.shift();
      this.ctx.moveTo(point1.X, point1.Y);
      this.ctx.lineTo(point2.X, point2.Y);
      this.ctx.stroke();
      this.ctx.draw(true);
    },
    clear() {
      let that = this;
      this.scaleValue = 1
      this.isEdit = false
      this.movableDisabled = false
      uni.getSystemInfo({
        success: function(res) {
          let canvasw = res.windowWidth;
          let canvash = res.windowHeight;
          that.ctx.clearRect(0, 0, canvasw, canvash);
          const pattern = that.ctx.createPattern(that.imageUrl, 'repeat-x')
          that.ctx.fillStyle = pattern
          that.dWidth = 285
          that.dHeight = 200
          that.ctx.setStrokeStyle('red')
          that.ctx.fillRect(0, 0, that.dWidth, that.dHeight)
          that.ctx.draw()
          // that.ctx.draw(true);
        }
      });
    },
    finish() {
      let that = this;
      uni.canvasToTempFilePath({
          canvasId: 'mycanvas',
          success: function(res) {
            // 这里的res.tempFilePath就是生成的签字图片
            // console.log('tempFilePath', res.tempFilePath);
            that.tempFilePath = res.tempFilePath
            that.$emit('onImgUrl', that.tempFilePath)
          }
      });
    },
    plusImageScalex() {
      const num = this.scaleValue + 0.4
      this.scaleValue = Math.floor(num * 100) / 100;
      // this.setImageScale(this.scaleValue);
    },
    minusImageScalex() {
      const num = this.scaleValue + 0.4
      this.scaleValue = - (Math.floor(num * 100) / 100);
      // this.setImageScale(-this.scaleValue);
    },
    // 设置图片缩放
    setImageScale(scale) {
      const that = this
      console.log('this.ctx.', this.ctx.dWidth, scale)
      // const value = this.imageContainer.scaleX + scale;
      // const zoom = Number(value.toFixed(2));
      // // 设置图片的缩放比例和位置
      // this.imageContainer.set({
      //   scaleX: zoom,
      //   scaleY: zoom,
      // });
      // this.canvasEle.renderAll();
      // that.ctx.fillRect(0, 0, 285, 200)
      // that.ctx.draw()
      const pattern = that.ctx.createPattern(that.imageUrl, 'repeat-x')
      that.ctx.fillStyle = pattern
      const w = that.dWidth * scale 
      const h = that.dHeight * scale
      console.log('this.ctx.',w, h)
      that.ctx.fillRect(0, 0, w, h)
      that.ctx.draw()
    },
    //点击事件 判断缩放比例 
    touchstart(e) {
      let x = e.touches[0].x
      let y = e.touches[0].y
      // this.node.forEach(item => {
      //   if (x > item.x * this.scale && x < (item.x + item.w) * this.scale
      //       && y > item.y * this.scale && y < (item.y + item.h) * this.scale) {
      //       //在范围内,根据标记定义节点类型
      //       // this.lookDetial(item)
      //   }
      // }) 
    },
    //s缩放比例
    scaleChange(e) {
      this.scaleValue = e.detail.scale
    },
    // 将base64图片转换为临时地址
    base64ToTempFilePath(base64Data, success, fail) {
      const fs = uni.getFileSystemManager()
      const fileName = 'temp_image_' + Date.now() + '.png' 
      // 自定义文件名,可根据需要修改
      const USER_DATA_PATH = 'ttfile://user' // uni.env.USER_DATA_PATH
      const filePath = USER_DATA_PATH + '/' + fileName
      const buffer = uni.base64ToArrayBuffer(base64Data)
      fs.writeFile({
        filePath,
        data: buffer,
        encoding: 'binary',
        success() {
          success && success(filePath)
        },
        fail() { fail && fail()}
      });
    },
    // base64转化成本地文件路径
    parseBlob(base64, success) {
      const arr = base64.split(',');
      console.log('parseBlob()-arr:', arr)
      const mime = arr[0].match(/:(.*?);/)[1];
      const bstr = atob(arr[1]);
      const n = bstr.length;
      const u8arr = new Uint8Array(n);
      for(let i = 0; i < n; i++) {
        u8arr[i] = bstr.charCodeAt(i);
      }
      // const url = URL || webkitURL;
      let a = new Blob([u8arr], {type: mime});
      const file = new File([a], 'test.png', {type: 'image/png'});
      console.log('parseBlob()-file', file);
      success && success(file)
    },
  }
};
</script>

<style lang="scss" scoped>
.pr{
  position: relative;
}
.canvas-frame {
  position: relative;
  width: 570rpx;
  // overflow: hidden;
  .icon-frame {
    position: absolute;
    top: 20rpx;
    right: 40rpx;
    z-index: 2;
  }
  .blockS{
    background: transparent;width: 570rpx; height: 400rpx;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
  }
  .icon-item {
    // font-size: 36rpx;
    // padding: 12rpx;
    // border-radius: 8rpx;
    // margin-right: 16rpx;
    // border: 1rpx solid #ccc;
    // background-color: #fff;

    &:hover {
      // background-color: #f1f1f1;
    }

    &:active {
      opacity: 0.8;
    }
  }
  .is-edit {
    color: #007EF3 !important;
  }
  .button-frame {
    position: absolute;
    bottom: 10rpx;
    right: 40rpx;
    z-index: 2;
  }

  #canvasElement {
    cursor: pointer;
  }
}
</style>

由于hbuildex-真机调试-打印很费劲,需要来回构建打包,从而找问题找了好久,其中因为 原生地图截屏返回的是纯base64的数据,未带 data:image\/.*;base64,然后找了半天的问题,需要一步步的推导和确认有没有错,错在那,花费了很多时间和精力;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/68333.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeaferUI - 性能强悍、简洁轻量的 HTML5 Canvas 2D 图形 UI 绘图框架,用于 web 端在线图形设计、图表、白板、数据可视化等场景

最近想做一个轻巧的在线画册和海报设计工具&#xff0c;最近发布的 LeaferUI 特别适合这样的场景。 LeaferUI 是什么&#xff1f; Leafer UI 是基于 LeaferJS 开发的一套绚丽多彩的 UI 绘图框架&#xff0c;帮助开发者快速生成图形界面。LeaferJS 是一个基于 HTML5 Canvas 开…

Spring Cloud构建微服务断路器介绍

什么是断路器 断路器模式源于Martin Fowler的Circuit Breaker一文。“断路器”本身是一种开关装置&#xff0c;用于在电路上保护线路过载&#xff0c;当线路中有电器发生短路时&#xff0c;“断路器”能够及时的切断故障电路&#xff0c;防止发生过载、发热、甚至起火等严重后果…

On Evaluation of Embodied Navigation Agents 论文阅读

论文信息 题目&#xff1a;On Evaluation of Embodied Navigation Agents 作者&#xff1a;Peter Anderson&#xff0c;Angel Chang 来源&#xff1a;arXiv 时间&#xff1a;2018 Abstract 过去两年&#xff0c;导航方面的创造性工作激增。这种创造性的输出产生了大量有时不…

Rust 原生支持龙架构指令集

导读近日&#xff0c;Rust 开源社区发布 1.71.0 版本&#xff0c;实现对龙架构&#xff08;LoongArch&#xff09;指令集的原生支持。 龙架构操作系统发行版和开发者可基于上游社区源代码构建或直接下载 Rust 开源社区发布的龙架构二进制版本。Rust 开发者将在龙架构平台上获得…

beego实现文件上传到七牛云详细教程

文章目录 安装获取凭证配置app.conf上代码调用示例ps 安装 执行命令&#xff1a; go get github.com/qiniu/go-sdk/v7获取凭证 Go SDK 的所有的功能&#xff0c;都需要合法的授权。授权凭证的签算需要七牛账号下的一对有效的Access Key和Secret Key&#xff0c;这对密钥可以…

系列七、RocketMQ如何保证顺序消费消息

一、概述 所谓顺序消费指的是可以按照消息的发送顺序来进行消费。例如一笔订单产生了3条消息&#xff0c;即下订单》减库存》增加订单&#xff0c;消费时要按照顺序消费才有意义&#xff0c;要不然就乱套了&#xff08;PS&#xff1a;你总不能订单还没下&#xff0c;就开始减库…

Vue-2.nodejs的介绍和安装

nodejs简介 ► 创建 Node.js 应用:package.json 首先&#xff0c;创建一个新文件夹以便于容纳需要的所有文件&#xff0c;并且在此其中创建一个 package.json 文件&#xff0c;描述你应用程序以及需要的依赖&#xff1a; 配合着你的 package.json 请运行 npm install。如果你…

基于ffmpeg与SDL的视频播放库

由于工作需要&#xff0c;自己封装的基于ffmpeg的视频编解码库&#xff0c;显示采用了SDL库。可以播放本地文件或网络流&#xff0c;支持多端口播放&#xff0c;支持文字叠加&#xff0c;截图、视频录制等等。 头文件代码&#xff1a; #pragma once #ifdef __DLLEXPORT #defin…

SpringCloud实用篇5——elasticsearch基础

目录 1.初识elasticsearch1.1 了解ES1.1.1 elasticsearch的作用1.1.2 ELK技术栈1.1.3 elasticsearch和lucene1.1.4 总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3 es的一些概念1.3.1 文档和字段1.3.2 索引和映射1.3.3 mysql与elasticsearch 1.4 部署单点…

flutter开发实战-TextPainter计算文本内容的宽度

flutter开发实战-TextPainter计算文本内容的宽度 最近开发过程中根据Text文本的大小判断是否需要进行显示跑马灯效果&#xff0c;获取文本的大小&#xff0c;需要TextPainter来获取Size 一、TextPainter TextPainter主要用于实现文本的绘制。TextPainter类可以将TextSpan渲染…

浏览器多管闲事之跨域

年少时的梦想就是买一台小霸王游戏机 当时的宣传语就是小霸王其乐无穷~。 大些了&#xff0c;攒够了零花钱&#xff0c;在家长的带领下终于买到了 那一刻我感觉就是最幸福的人 风都是甜的&#xff01; 哪成想... 刚到家就被家长扣下了 “”禁止未成年人玩游戏机 (问过卖家了&a…

Window下安装MinGW64

欢迎来到我的酒馆 介绍Windows下&#xff0c;安装MinGW64。 目录 欢迎来到我的酒馆二.MinGW64三.配置系统环境变量 二.MinGW64 从sourceforge下载mingw64&#xff0c; sourceforge下载MinGW https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/ 下…

香港站群服务器为什么适合seo优化?

​  香港站群为什么适合seo优化?本文主要从以下四点出发进行原因阐述。 1.香港站群服务器的优势 2.香港站群服务器与国内服务器的对比 3.多IP站群服务器的优势 4.香港站群服务器在SEO优化中的注意事项 1.香港站群服务器的优势 香港站群服务器是为了满足企业SEO优化需求而提供…

实践分享:Vue 项目如何迁移小程序

最近我们小组刚经历了将成熟的 HTML5 项目转换成小程序&#xff0c;并在app中运行的操作&#xff01;记录下来分享给各位。 项目&#xff1a;将已有的 Vue 项目转为小程序&#xff0c; 在集成了FinClip SDK 的 App 中运行。 技术&#xff1a;uni-app、FinClip 两个注意事项&…

[每周一更]-(第57期):用Docker、Docker-compose部署一个完整的前后端go+vue分离项目

文章目录 1.参考项目2.技能点3.GO的Dockerfile配置后端的结构如图Dockerfile先手动docker调试服务是否可以启动报错 4.Vue的Dockerfile配置前端的结构如图nginx_docker.confDockerfile构建 5.docker-compose 整合前后端docker-compose.yml错误记录&#xff08;1&#xff09;ip端…

PS AI版本安装教程

好久没写博客了&#xff0c;今天更新一下子吧&#xff01; 随着chatGPT的提出&#xff0c;各种软件逐渐开始镶嵌人工智能&#xff0c;为我们的生活带来了极大的便利&#xff01;话不多说&#xff0c;开始介绍今天的主角&#xff0c;PS的AI版本。 安装教程&#xff1a; 1.安装…

Docker一键部署项目,无需登录XShell

文章目录 一键部署项目Docker手动部署SpringBoot项目编写docker部署的脚本文件script.sh 脚本内容 特别注意&#xff01;编写dockerfiledockerfile 文件内容 上传后端服务的jar包到服务器中执行 script 脚本部署后端服务 自动部署SpringBoot项目引入jsch依赖编写jsch工具类执行…

工业4.0:欢迎来到智能制造

制造业正在经历一场被称为“工业4.0”的全新技术革命&#xff0c;这场革命将数字化、网络化、智能化和自动化技术融合在一起&#xff0c;旨在打造高质、高效、高产且可持续的智能工厂。工业4.0将彻底改变产品制造的方式&#xff0c;颠覆我们对制造业的传统认知。 什么是工业4.…

【Java】try|catch|throws 具体详解+应用

目录 tryCatch 基本介绍 使用细节 throws异常处理 基本介绍 ​ 使用细节 自定义异常 基本概念 步骤 throw和throws的区别 tryCatch 基本介绍 使用细节 throws异常处理 基本介绍 使用细节 自定义异常 基本概念 步骤 throw和throws的区别

【数据结构】Disruptor环形数组无锁并发框架阅读

Disruptor 是苹国外厂本易公司LMAX开发的一个高件能列&#xff0c;研发的初夷是解决内存队列的延识问顾在性能测试中发现竟然与10操作处于同样的数量级)&#xff0c;基于Disruptor开发的系统单线程能支撑每秒600万订单&#xff0c;2010年在QCn演讲后&#xff0c;获得了业界关注…