【JS】图片裁剪上传

news2024/11/17 10:34:19

前言

流程如下:本地预览 => 裁剪 => 上传

实现

1. 本地预览

将数据读为 dataurl 赋值给 img 标签的 src

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .container {
      display: flex;
    }

    #image-container {
      position: relative;
      width: 400px;
      height: 400px;
      border: 1px solid #ccc;
      background-color: #f3f3f3;
    }

    #result {
      margin-left: 20px;
      border: 1px solid #ccc;
      width: 200px;
      height: 200px;
      background-color: #f3f3f3;
    }
  </style>
</head>

<body>
  <div>
    <input type="file" id="file-input">
  </div>
  <div class="container">
    <div id="image-container">
      <img id="uploaded-image" style="width: 100%; height: 100%;" />
    </div>
    <canvas id="result" width="200" height="200"></canvas> <!-- 显示裁剪结果的画布 -->
  </div>

  <script>
    const fileInput = document.getElementById('file-input');
    const uploadedImage = document.getElementById('uploaded-image');

    fileInput.onchange = (e) => {
      const file = e.target.files[0];
      const reader = new FileReader();
      reader.onload = (e) => {
        uploadedImage.src = e.target.result;
      }
      reader.readAsDataURL(file);
    }
  </script>
</body>

</html>

2. 裁剪

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片裁剪预览</title>
  <style>
    .container {
      display: flex;
    }

    #image-container {
      position: relative;
      width: 400px;
      height: 400px;
      border: 1px solid #ccc;
      background-color: #f3f3f3;
    }

    #crop-box {
      position: absolute;
      border: 2px dashed #ff0000;
      cursor: move;
      display: none;
    }

    #crop-box .resize-handle {
      position: absolute;
      width: 10px;
      height: 10px;
      background: #ff0000;
      z-index: 10;
    }

    #crop-box .top-left {
      left: -5px;
      top: -5px;
      cursor: nwse-resize;
    }

    #crop-box .top-right {
      right: -5px;
      top: -5px;
      cursor: nesw-resize;
    }

    #crop-box .bottom-left {
      left: -5px;
      bottom: -5px;
      cursor: nesw-resize;
    }

    #crop-box .bottom-right {
      right: -5px;
      bottom: -5px;
      cursor: nwse-resize;
    }

    #result {
      margin-left: 20px;
      border: 1px solid #ccc;
      width: 200px;
      height: 200px;
      background-color: #f3f3f3;
    }
  </style>
</head>

<body>

  <div>
    <input type="file" id="file-input">
  </div>
  <div class="container">
    <div id="image-container">
      <img id="uploaded-image" style="max-width: 100%; max-height: 100%; display: none;" />
      <div id="crop-box">
        <div class="resize-handle top-left"></div> <!-- 左上角调整手柄 -->
        <div class="resize-handle top-right"></div> <!-- 右上角调整手柄 -->
        <div class="resize-handle bottom-left"></div> <!-- 左下角调整手柄 -->
        <div class="resize-handle bottom-right"></div> <!-- 右下角调整手柄 -->
      </div>
    </div>
    <canvas id="result" width="200" height="200"></canvas> <!-- 显示裁剪结果的画布 -->
  </div>

  <script>
    const fileInput = document.getElementById('file-input');
    const uploadedImage = document.getElementById('uploaded-image');
    const cropBox = document.getElementById('crop-box');
    const resultCanvas = document.getElementById('result');
    const ctx = resultCanvas.getContext('2d');
    const commitBtn = document.getElementById('commit-btn');

    let image = new Image();
    let cropInfo = { x: 0, y: 0, width: 100, height: 100 }; // 裁剪框的位置和大小
    let scale = 1; // 图片缩放比例

    let isDragging = false; // 是否正在拖动裁剪框
    let isResizing = false; // 是否正在调整裁剪框大小
    let resizingHandle = null; // 当前调整手柄
    const MIN_CROP_SIZE = 20; // 裁剪框的最小尺寸

    fileInput.addEventListener('change', (event) => {
      const file = event.target.files[0];
      const reader = new FileReader();

      reader.onload = function (e) {
        image.src = e.target.result;
        uploadedImage.src = e.target.result;
        uploadedImage.style.display = 'block';
      }

      reader.readAsDataURL(file);
    });

    image.onload = function () {
      // 获取容器宽高
      const container = document.getElementById('image-container');
      const containerWidth = container.offsetWidth;
      const containerHeight = container.offsetHeight;

      scale = Math.min(containerWidth / image.width, containerHeight / image.height); // 计算缩放比例

      uploadedImage.style.width = image.width * scale + 'px'; // 根据缩放比例设置上传图片的宽度
      uploadedImage.style.height = image.height * scale + 'px'; // 根据缩放比例设置上传图片的高度

      // 重置裁剪框的位置和大小
      cropInfo = { x: 0, y: 0, width: 100, height: 100 };

      updateCropBox(); // 更新裁剪框位置和大小
      cropBox.style.display = 'block'; // 显示裁剪框

      drawCrop(); // 绘制裁剪结果
    }

    // 更新裁剪框的位置和大小
    function updateCropBox() {
      cropBox.style.left = cropInfo.x + 'px';
      cropBox.style.top = cropInfo.y + 'px';
      cropBox.style.width = cropInfo.width + 'px';
      cropBox.style.height = cropInfo.height + 'px';
    }

    // 绘制裁剪结果
    function drawCrop() {
      const cropX = cropInfo.x / scale;
      const cropY = cropInfo.y / scale;
      const cropWidth = cropInfo.width / scale;
      const cropHeight = cropInfo.height / scale;

      ctx.clearRect(0, 0, resultCanvas.width, resultCanvas.height); // 清空画布
      ctx.drawImage(image, cropX, cropY, cropWidth, cropHeight, 0, 0, resultCanvas.width, resultCanvas.height); // 绘制裁剪的图片
    }

    cropBox.addEventListener('mousedown', function (e) {
      if (e.target.classList.contains('resize-handle')) {
        // 如果按下的是调整手柄
        isResizing = true;
        resizingHandle = e.target; // 当前调整手柄
      } else {
        // 如果按下的是裁剪框,记录鼠标相对裁剪框的坐标
        isDragging = true;
        offsetX = e.offsetX;
        offsetY = e.offsetY;
      }
      document.addEventListener('mousemove', mouseMove); // 添加鼠标移动事件
      document.addEventListener('mouseup', mouseUp); // 添加鼠标抬起事件
    });

    function mouseMove(e) {
      if (isDragging) {
        // 正在拖动裁剪框,更新裁剪框坐标
        cropInfo.x = e.clientX - offsetX - image.getBoundingClientRect().left;
        cropInfo.y = e.clientY - offsetY - image.getBoundingClientRect().top;
        updateCropBox(); // 更新裁剪框
        drawCrop(); // 绘制裁剪结果
      }

      if (isResizing) {
        // 正在调整裁剪框大小
        const handleClass = resizingHandle.classList[1]; // 获取调整手柄的类名
        if (handleClass.includes('top-left')) {
          // 左上角手柄
          const newWidth = cropInfo.width + (cropInfo.x - e.clientX + image.getBoundingClientRect().left);
          const newHeight = cropInfo.height + (cropInfo.y - e.clientY + image.getBoundingClientRect().top);
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.x = e.clientX - image.getBoundingClientRect().left;
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.y = e.clientY - image.getBoundingClientRect().top;
            cropInfo.height = newHeight;
          }
        } else if (handleClass.includes('top-right')) {
          // 右上角手柄
          const newWidth = e.clientX - cropInfo.x - image.getBoundingClientRect().left;
          const newHeight = cropInfo.height + (cropInfo.y - e.clientY + image.getBoundingClientRect().top);
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.y = e.clientY - image.getBoundingClientRect().top;
            cropInfo.height = newHeight;
          }
        } else if (handleClass.includes('bottom-left')) {
          // 左下角手柄
          const newWidth = cropInfo.width + (cropInfo.x - e.clientX + image.getBoundingClientRect().left);
          const newHeight = e.clientY - cropInfo.y - image.getBoundingClientRect().top;
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.x = e.clientX - image.getBoundingClientRect().left;
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.height = newHeight;
          }
        } else if (handleClass.includes('bottom-right')) {
          // 右下角手柄
          const newWidth = e.clientX - cropInfo.x - image.getBoundingClientRect().left;
          const newHeight = e.clientY - cropInfo.y - image.getBoundingClientRect().top;
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.height = newHeight;
          }
        }

        updateCropBox(); // 更新裁剪框
        drawCrop(); // 绘制裁剪结果
      }
    }

    function mouseUp() {
      isDragging = false; // 停止拖动
      isResizing = false; // 停止调整大小
      document.removeEventListener('mousemove', mouseMove); // 移除鼠标移动事件
      document.removeEventListener('mouseup', mouseUp); // 移除鼠标抬起事件
    }
  </script>
</body>

</html>

3. 上传

通过 canvas 生成 blob 再变为 File

resultCanvas.toBlob((blob) => {
  const file = new File([blob], 'cut.png', { type: 'image/png' });
  console.log(file); // 执行上传逻辑即可
})

整体代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片裁剪预览</title>
  <style>
    .container {
      display: flex;
    }

    #image-container {
      position: relative;
      width: 400px;
      height: 400px;
      border: 1px solid #ccc;
      background-color: #f3f3f3;
    }

    #crop-box {
      position: absolute;
      border: 2px dashed #ff0000;
      cursor: move;
      display: none;
    }

    #crop-box .resize-handle {
      position: absolute;
      width: 10px;
      height: 10px;
      background: #ff0000;
      z-index: 10;
    }

    #crop-box .top-left {
      left: -5px;
      top: -5px;
      cursor: nwse-resize;
    }

    #crop-box .top-right {
      right: -5px;
      top: -5px;
      cursor: nesw-resize;
    }

    #crop-box .bottom-left {
      left: -5px;
      bottom: -5px;
      cursor: nesw-resize;
    }

    #crop-box .bottom-right {
      right: -5px;
      bottom: -5px;
      cursor: nwse-resize;
    }

    #result {
      margin-left: 20px;
      border: 1px solid #ccc;
      width: 200px;
      height: 200px;
      background-color: #f3f3f3;
    }

    #commit-btn {
      margin-left: 20px;
      border: 1px solid #ccc;
      width: 60px;
      height: 30px;
    }
  </style>
</head>

<body>

  <div>
    <input type="file" id="file-input">
  </div>
  <div class="container">
    <div id="image-container">
      <img id="uploaded-image" style="max-width: 100%; max-height: 100%; display: none;" />
      <div id="crop-box">
        <div class="resize-handle top-left"></div> <!-- 左上角调整手柄 -->
        <div class="resize-handle top-right"></div> <!-- 右上角调整手柄 -->
        <div class="resize-handle bottom-left"></div> <!-- 左下角调整手柄 -->
        <div class="resize-handle bottom-right"></div> <!-- 右下角调整手柄 -->
      </div>
    </div>
    <canvas id="result" width="200" height="200"></canvas> <!-- 显示裁剪结果的画布 -->
    <button id="commit-btn">上传</button>
  </div>

  <script>
    const fileInput = document.getElementById('file-input');
    const uploadedImage = document.getElementById('uploaded-image');
    const cropBox = document.getElementById('crop-box');
    const resultCanvas = document.getElementById('result');
    const ctx = resultCanvas.getContext('2d');
    const commitBtn = document.getElementById('commit-btn');

    let image = new Image();
    let cropInfo = { x: 0, y: 0, width: 100, height: 100 }; // 裁剪框的位置和大小
    let scale = 1; // 图片缩放比例

    let isDragging = false; // 是否正在拖动裁剪框
    let isResizing = false; // 是否正在调整裁剪框大小
    let resizingHandle = null; // 当前调整手柄
    const MIN_CROP_SIZE = 20; // 裁剪框的最小尺寸

    fileInput.addEventListener('change', (event) => {
      const file = event.target.files[0];
      const reader = new FileReader();

      reader.onload = function (e) {
        image.src = e.target.result;
        uploadedImage.src = e.target.result;
        uploadedImage.style.display = 'block';
      }

      reader.readAsDataURL(file);
    });

    image.onload = function () {
      // 获取容器宽高
      const container = document.getElementById('image-container');
      const containerWidth = container.offsetWidth;
      const containerHeight = container.offsetHeight;

      scale = Math.min(containerWidth / image.width, containerHeight / image.height); // 计算缩放比例

      uploadedImage.style.width = image.width * scale + 'px'; // 根据缩放比例设置上传图片的宽度
      uploadedImage.style.height = image.height * scale + 'px'; // 根据缩放比例设置上传图片的高度

      // 重置裁剪框的位置和大小
      cropInfo = { x: 0, y: 0, width: 100, height: 100 };

      updateCropBox(); // 更新裁剪框位置和大小
      cropBox.style.display = 'block'; // 显示裁剪框

      drawCrop(); // 绘制裁剪结果
    }

    // 更新裁剪框的位置和大小
    function updateCropBox() {
      cropBox.style.left = cropInfo.x + 'px';
      cropBox.style.top = cropInfo.y + 'px';
      cropBox.style.width = cropInfo.width + 'px';
      cropBox.style.height = cropInfo.height + 'px';
    }

    // 绘制裁剪结果
    function drawCrop() {
      const cropX = cropInfo.x / scale;
      const cropY = cropInfo.y / scale;
      const cropWidth = cropInfo.width / scale;
      const cropHeight = cropInfo.height / scale;

      ctx.clearRect(0, 0, resultCanvas.width, resultCanvas.height); // 清空画布
      ctx.drawImage(image, cropX, cropY, cropWidth, cropHeight, 0, 0, resultCanvas.width, resultCanvas.height); // 绘制裁剪的图片
    }

    cropBox.addEventListener('mousedown', function (e) {
      if (e.target.classList.contains('resize-handle')) {
        // 如果按下的是调整手柄
        isResizing = true;
        resizingHandle = e.target; // 当前调整手柄
      } else {
        // 如果按下的是裁剪框,记录鼠标相对裁剪框的坐标
        isDragging = true;
        offsetX = e.offsetX;
        offsetY = e.offsetY;
      }
      document.addEventListener('mousemove', mouseMove); // 添加鼠标移动事件
      document.addEventListener('mouseup', mouseUp); // 添加鼠标抬起事件
    });

    function mouseMove(e) {
      if (isDragging) {
        // 正在拖动裁剪框,更新裁剪框坐标
        cropInfo.x = e.clientX - offsetX - image.getBoundingClientRect().left;
        cropInfo.y = e.clientY - offsetY - image.getBoundingClientRect().top;
        updateCropBox(); // 更新裁剪框
        drawCrop(); // 绘制裁剪结果
      }

      if (isResizing) {
        // 正在调整裁剪框大小
        const handleClass = resizingHandle.classList[1]; // 获取调整手柄的类名
        if (handleClass.includes('top-left')) {
          // 左上角手柄
          const newWidth = cropInfo.width + (cropInfo.x - e.clientX + image.getBoundingClientRect().left);
          const newHeight = cropInfo.height + (cropInfo.y - e.clientY + image.getBoundingClientRect().top);
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.x = e.clientX - image.getBoundingClientRect().left;
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.y = e.clientY - image.getBoundingClientRect().top;
            cropInfo.height = newHeight;
          }
        } else if (handleClass.includes('top-right')) {
          // 右上角手柄
          const newWidth = e.clientX - cropInfo.x - image.getBoundingClientRect().left;
          const newHeight = cropInfo.height + (cropInfo.y - e.clientY + image.getBoundingClientRect().top);
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.y = e.clientY - image.getBoundingClientRect().top;
            cropInfo.height = newHeight;
          }
        } else if (handleClass.includes('bottom-left')) {
          // 左下角手柄
          const newWidth = cropInfo.width + (cropInfo.x - e.clientX + image.getBoundingClientRect().left);
          const newHeight = e.clientY - cropInfo.y - image.getBoundingClientRect().top;
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.x = e.clientX - image.getBoundingClientRect().left;
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.height = newHeight;
          }
        } else if (handleClass.includes('bottom-right')) {
          // 右下角手柄
          const newWidth = e.clientX - cropInfo.x - image.getBoundingClientRect().left;
          const newHeight = e.clientY - cropInfo.y - image.getBoundingClientRect().top;
          // 如果新宽高合理,则更新裁剪框位置
          if (newWidth > MIN_CROP_SIZE) {
            cropInfo.width = newWidth;
          }
          if (newHeight > MIN_CROP_SIZE) {
            cropInfo.height = newHeight;
          }
        }

        updateCropBox(); // 更新裁剪框
        drawCrop(); // 绘制裁剪结果
      }
    }

    function mouseUp() {
      isDragging = false; // 停止拖动
      isResizing = false; // 停止调整大小
      document.removeEventListener('mousemove', mouseMove); // 移除鼠标移动事件
      document.removeEventListener('mouseup', mouseUp); // 移除鼠标抬起事件
    }

    commitBtn.addEventListener('click', function () {
      resultCanvas.toBlob((blob) => {
        const file = new File([blob], 'cut.png', { type: 'image/png' });
        console.log(file); // 执行上传逻辑即可
      })
    })
  </script>
</body>

</html>

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

近两年ATECLOUD都更新过哪些功能模块?

ATECLOUD作为一款智能化测试平台&#xff0c;不仅可以满足开关电源自动测试的需求&#xff0c;还可以基于平台搭建电源芯片、射频组件以及定制化测试方案&#xff0c;为了满足各类方案的测试需求&#xff0c;ATECLOUD平台也在不断功能更新迭代中&#xff0c;从2018年诞生以来&a…

Java抽象类与接口详解

目录 &#x1f54a;抽象类&#x1f4da;1.概念&#x1f4da;2.语法&#x1f4da;3.特性&#x1f4da;4.作用 &#x1f54a;接口&#x1f334;1.概念引入&#x1f334;2.语法规则&#x1f334;3.特性&#x1f334;4.使用&#x1f33b;5.实现多个接口&#x1f33b;6.接口间的继承…

[数据集][目标检测]手机识别检测数据集VOC+YOLO格式9997张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;9997 标注数量(xml文件个数)&#xff1a;9997 标注数量(txt文件个数)&#xff1a;9997 标注…

祝贺!这些学校新增测绘、遥感、地理、城乡规划硕博学位点!

国务院学位委员会办公室近日公示2024年新增博士硕士学位授权审核专家核查及评议结果&#xff0c;其中涉及测绘科学与技术、遥感科学与技术、地理学、城乡规划、地理学、资源与环境专业院校名单如下&#xff1a; 新增硕士学位授权点审核结果 测绘科学与技术 南京林业大学 西南…

【JavaEE初阶】深入解析死锁的产生和避免以及内存不可见问题

前言&#xff1a; &#x1f308;上期博客&#xff1a;【后端开发】JavaEE初阶—线程安全问题与加锁原理&#xff08;超详解&#xff09;-CSDN博客 &#x1f525;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 ⭐️小编会在后端开发的学习中不断更新~~~ &#…

进程的那些事--进程控制

目录 前言 一、创建进程 二、退出进程 void exit (int retval) 三、进程等待 四、进程替换 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 前面我们认识了进程&#xff0c;现在让我们认识几个进程的接口 提示&#xff1a;以下是本篇文章正文内容…

MySQL_表_进阶(2/2)

上一章我们谈了排序子句&#xff0c;使用ORDER BY 字段 DESC/ASC。以及左右连接的多关系查询。 今天&#xff0c;没错&#xff0c;四张表最后两个需求 ✨涉及聚合函数查询与指定别名 四张表&#xff1a; 学院表&#xff1a;(testdb.dept) 课程表&#xff1a;(testdb.course) 选…

MT5016A-ASEMI三相整流桥MT5016A

编辑&#xff1a;ll MT5016A-ASEMI三相整流桥MT5016A 型号&#xff1a;MT5016A 品牌&#xff1a;ASEMI 封装&#xff1a;D-63 批号&#xff1a;2024 类型&#xff1a;三相整流桥 电流&#xff08;ID&#xff09;&#xff1a;50A 电压(VF)&#xff1a;1600V 安装方式&a…

示例说明:elasticsearch实战应用

Elasticsearch 是一个基于 Lucene 的分布式搜索和分析引擎&#xff0c;广泛应用于日志分析、全文搜索、数据可视化等领域。以下是 Elasticsearch 实战应用的一些关键点和步骤&#xff1a; 1. 环境搭建 首先&#xff0c;你需要在你的环境中安装和配置 Elasticsearch。 安装 E…

K8S精进之路-控制器StatefulSet有状态控制 -(2)

状态说明 在进行StatefulSet部署之前&#xff0c;我们首先可能要了解一下&#xff0c;什么是"有状态应用"和"无状态应用"。无状态应用就是pod无论部署在哪里&#xff0c;在哪台服务器上提供服务&#xff0c;都是一样的结果&#xff0c;比如经常用的nginx。…

Django5 使用pyinstaller打包成 exe服务

首先&#xff1a;确保当前的django项目可以完美运行&#xff0c;再进行后续操作 python manage.py runserver第一步 安装 pyinstaller pip install pyinstaller第二步 创建spec 文件 pyinstaller --name manage --onefile manage.pypyinstaller&#xff1a;这是调用 PyInsta…

SpringBoot 流式输出时,正常输出后为何突然报错?

一个 SpringBoot 项目同时使用了 Tomcat 的过滤器和 Spring 的拦截器&#xff0c;一些线程变量在过滤器中初始化并在拦截器中使用。 该项目需要调用大语言模型进行流式输出。 项目中&#xff0c;笔者使用 SpringBoot 的 ResponseEntity<StreamingResponseBody> 将流式输…

【YOLO目标检测马铃薯叶病害数据集】共1912张、已标注txt格式、有训练好的yolov5的模型

目录 说明图片示例 说明 数据集格式&#xff1a;YOLO格式 图片数量&#xff1a;1912 标注数量(txt文件个数)&#xff1a;1912 标注类别数&#xff1a;5 标注类别名称&#xff1a; health General early blight Severe early blight General late blight Severe late bligh…

Vue3使用vue-quill富文本编辑器实现图片大小调整

安装uill-image-resize npm install quill-image-resize --save在项目中导入并注册插件 import { QuillEditor, Quill } from vueup/vue-quill; import ImageUploader from quill-image-uploader; import ImageResize from quill-image-resize; //导入插件 import vueup/vue-…

webservice xfire升级为cxf cxf常用注解 cxf技术点 qualified如何设置

关键点 确保参数名称保持一致确保参数命名空间保持一致确保接口命名空间保持一致确保请求头设置正确确保用soapui工具解析的参数结构一致 cxf常用注解 定义接口用到的注解 定义接口名称&#xff0c;和接口命名空间 WebService(name“ams” ,targetNamespace “http://ifac…

海山数据库(He3DB)+AI(五):一种基于强化学习的数据库旋钮调优方法

[TOC] 0 前言 在海山数据库(He3DB)AI&#xff08;三&#xff09;中&#xff0c;介绍了四种旋钮调优方法&#xff1a;基于启发式&#xff0c;基于贝叶斯&#xff0c;基于深度学习和基于强化学习。本文介绍一种基于强化学习的旋钮调优方法&#xff1a;QTune: A Query-Aware Dat…

回归预测 | Matlab基于SO-ESN蛇群算法优化回声状态网络多输入单输出回归预测

回归预测 | Matlab基于SO-ESN蛇群算法优化回声状态网络多输入单输出回归预测 目录 回归预测 | Matlab基于SO-ESN蛇群算法优化回声状态网络多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.蛇群算法(SO)优化回声状态网络做拟合回归预测&#xff0c;…

Spring Security - 用户授权

1.用户授权介绍&#xff1a; 在SpringSecurity中&#xff0c;会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication&#xff0c;然后获取其中的权限信息。判断当前用户是否拥有访问当前资源…

汉口银行IPO之路再添坎坷:多名股东甩卖股权,内控是“老大难”

撰稿|芋圆 近日&#xff0c;总部设在武汉的商业银行——汉口银行股份有限公司&#xff08;以下简称“汉口银行”&#xff09;的股权再现交易信息&#xff0c;先后“亮相” 上海联合产权交易所、北京产权交易所&#xff0c;出售股权的股东包括中国电信以及中国移动全资子公司等…