SpringBoot实现的大文件上传

前言

大文件分片上传和断点续传是为了解决在网络传输过程中可能遇到的问题,以提高文件传输的效率和稳定性。

  • 首先,大文件分片上传是将大文件分割成较小的片段进行上传。这样做的好处是可以减少单个文件的传输时间,因为较小的文件片段更容易快速上传到目标服务器。同时,如果在传输过程中出现错误或中断,只需要重新上传出现问题的文件片段,而不需要重新上传整个文件,从而减少了传输的时间和带宽消耗。
  • 其次,断点续传是指在文件传输过程中,如果传输被中断或者发生错误,可以从上一次中断的地方继续传输,而不是从头开始。这对于大文件的传输尤为重要,因为传输一个大文件可能需要较长的时间,而中断可能是由网络问题、电源故障、软件崩溃或其他因素引起的。断点续传功能允许用户在中断后恢复传输,而无需重新开始,节省了时间和资源。

大文件分片上传和断点续传在以下情况下尤为重要:

  1. 低带宽网络环境:在网络速度较慢或不稳定的情况下,将大文件分割为较小的片段进行上传可以降低传输的时间和失败的风险。
  2. 大文件传输:对于大文件,一次性完整上传可能需要很长时间,而且中途出现问题时需要重新传输整个文件,因此将文件分割并实现断点续传功能可以提高效率和可靠性。
  3. 网络中断或传输错误:网络中断、电源故障或软件崩溃等因素可能导致文件传输中断,断点续传功能可以从中断处恢复,避免重新传输整个文件。
  4. 多用户并发上传:在有多个用户同时上传文件的情况下,分片上传和断点续传可以减少对服务器资源的占用,提高并发传输的效率。

前端

采用百度的webuploader,在file.html中引用webuploader.js、jquery.js

代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>大文件上传下载</title><link rel="stylesheet" type="text/css" href="webuploader.css"><script src="jquery.js"></script><script src="webuploader.js"></script><style>#upload-container {width: 100px;height: 50px;background: #94d3e7;padding-bottom: 10px;}</style>
</head>
<body><div id="upload-container"><span>文件拖拽上传</span></div><button id="picker" style="margin-top: 20px">分片上传</button><div id="upload-list"></div><hr/>
<a href="/file/download" >普通下载</a>
<hr/>
<a href="/file/downloads" target="_blank">分片下载</a></body>
<script>$('#upload-container').click(function (event) {$("#picker").find('input').click();});// 初始化上传组件const uploader = WebUploader.create({auto: true,swf: 'Uploader.swf', // swf文件路径server: '/file/upload', // 上传接口dnd: '#upload-container',pick: '#picker',  // 内部根据当前运行创建multiple: true,     // 选择多个chunked: true,      // 开启分片threads: 8,        // 并发数,默认 3chunkRetry: 8,         // 如果遇到网络错误,重新上传次数method: 'POST',fileSizeLimit: 1024 * 1024 * 1024 * 10, // 文件总大小为10GfileSingleSizeLimit: 1024 * 1024 * 1024 * 1,  // 单个文件大小最大为1GfileVal: 'upload'});// 入队之前触发事件uploader.on("beforeFileQueued", function (file) {// 获取文件后缀console.log(file.name);});// 当有文件被添加进队列的时候uploader.on('fileQueued', function (file) {$('#upload-list').append( '<div id="' + file.id + '" class="item">' +'<h4 class="info">' + file.name + '</h4>' +'<p class="state">等待上传...</p>' +'</div>' );});// 文件上传过程中创建进度条实时显示。uploader.on('uploadProgress', function (file, percentage) {var $li = $('#' + file.id),$percent = $li.find('.progress .progress-bar');// 避免重复创建if (!$percent.length) {$percent = $('<div class="progress progress-striped active">' +'<div class="progress-bar" role="progressbar" style="width: 0%">' +'</div>' +'</div>').appendTo($li).find('.progress-bar');}$li.find('p.state').text('上传中');$percent.css('width', percentage * 100 + '%');});uploader.on( 'uploadSuccess', function( file ) {$( '#'+file.id ).find('p.state').text('已上传');});uploader.on( 'uploadError', function( file ) {$( '#'+file.id ).find('p.state').text('上传出错');});uploader.on( 'uploadComplete', function( file ) {$( '#'+file.id ).find('.progress').fadeOut();});</script>
</html>

后端

1.Pom文件添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.4</version><relativePath/></parent><groupId>com.liyh</groupId><artifactId>springboot-file</artifactId><version>0.0.1</version><name>springboot-file</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.4</version></dependency><!-- 做断点下载使用 --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
2.yml配置文件
# 配置服务端口
server:port: 8018spring:servlet:multipart:# Spring Boot中有默认的文件上传组件,在使用ServletFileUpload时需要关闭Spring Boot的默认配置enabled: false# 设置单个文件大小max-file-size: 1GB# 设置单次请求文件的总大小max-request-size: 10GB

3..编写测试接口controller

package com.liyh.controller;import com.liyh.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 文件上传测试接口** @author liyh*/
@RestController
@RequestMapping("/file")
public class FileController {@Autowiredprivate FileService fileService;/*** 单个文件上传,支持断点续传*/@PostMapping("/upload")public void upload(HttpServletRequest request, HttpServletResponse response) {try {fileService.upload(request, response);} catch (Exception e) {e.printStackTrace();}}/*** 普通文件下载*/@GetMapping("/download")public void download(HttpServletRequest request, HttpServletResponse response) throws IOException {fileService.download(request, response);}/*** 分片文件下载*/@GetMapping("/downloads")public String downloads() throws IOException {fileService.downloads();return "下载成功";}}

4.编写service 

package com.liyh.service;import com.liyh.entity.DownloadFileInfo;
import com.liyh.entity.FileInfo;
import com.liyh.entity.UploadFileInfo;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;@Service
public class FileService {/*** 编码*/private static final String UTF_8 = "UTF-8";/*** 文件上传路径(当前项目路径下,也可配置固定路径)*/private String uploadPath = System.getProperty("user.dir") + "/springboot-file/upload/";/*** 下载指定文件*/private String downloadFile = "D:\\Download\\git.exe";/*** 文件下载地址(当前项目路径下,也可配置固定路径)*/private String downloadPath = System.getProperty("user.dir") + "/springboot-file/download/";/*** 分片下载每一片大小为50M*/private static final Long PER_SLICE = 1024 * 1024 * 50L;/*** 定义分片下载线程池*/private ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());/*** final string*/private static final String RANGE = "Range";/*** 上传文件*/public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception {// 获取ServletFileUploadServletFileUpload servletFileUpload = getServletFileUpload();List<FileItem> items = servletFileUpload.parseRequest(request);// 获取文件信息UploadFileInfo uploadFileInfo = getFileInfo(items);// 写入临时文件writeTempFile(items, uploadFileInfo);// 判断是否合并mergeFile(uploadFileInfo);// 返回结果response.setCharacterEncoding(UTF_8);response.getWriter().write("上传成功");}/*** 获取ServletFileUpload*/private ServletFileUpload getServletFileUpload() {// 设置缓冲区大小,先读到内存里在从内存写DiskFileItemFactory factory = new DiskFileItemFactory();factory.setSizeThreshold(1024);File file = new File(uploadPath);// 如果文件夹不存在则创建if (!file.exists() && !file.isDirectory()) {file.mkdirs();}factory.setRepository(file);// 解析ServletFileUpload upload = new ServletFileUpload(factory);// 设置单个大小与最大大小upload.setFileSizeMax(1 * 1024 * 1024 * 1024L);upload.setSizeMax(10 * 1024 * 1024 * 1024L);return upload;}/*** 获取文件信息** @param items* @return* @throws UnsupportedEncodingException*/private UploadFileInfo getFileInfo(List<FileItem> items) throws UnsupportedEncodingException {UploadFileInfo uploadFileInfo = new UploadFileInfo();for (FileItem item : items) {if (item.isFormField()) {// 获取分片数据if ("chunk".equals(item.getFieldName())) {uploadFileInfo.setCurrentChunk(Integer.parseInt(item.getString(UTF_8)));}if ("chunks".equals(item.getFieldName())) {uploadFileInfo.setChunks(Integer.parseInt(item.getString(UTF_8)));}if ("name".equals(item.getFieldName())) {uploadFileInfo.setFileName(item.getString(UTF_8));}}}return uploadFileInfo;}/*** 写入临时文件** @param items* @param uploadFileInfo* @throws Exception*/private void writeTempFile(List<FileItem> items, UploadFileInfo uploadFileInfo) throws Exception {// 获取文件基本信息后for (FileItem item : items) {if (!item.isFormField()) {// 有分片需要临时目录String tempFileName = uploadFileInfo.getFileName();if (StringUtils.isNotBlank(tempFileName)) {if (uploadFileInfo.getCurrentChunk() != null) {tempFileName = uploadFileInfo.getCurrentChunk() + "_" + uploadFileInfo.getFileName();}// 判断文件是否存在File tempFile = new File(uploadPath, tempFileName);// 断点续传,判断文件是否存在,若存在则不传if (!tempFile.exists()) {item.write(tempFile);}}}}}/*** 判断是否合并** @param uploadFileInfo* @throws IOException* @throws InterruptedException*/private void mergeFile(UploadFileInfo uploadFileInfo) throws IOException, InterruptedException {Integer currentChunk = uploadFileInfo.getCurrentChunk();Integer chunks = uploadFileInfo.getChunks();String fileName = uploadFileInfo.getFileName();// 如果当前分片等于总分片那么合并文件if (currentChunk != null && chunks != null && currentChunk.equals(chunks - 1)) {File tempFile = new File(uploadPath, fileName);try (BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile))) {// 根据之前命名规则找到所有分片for (int i = 0; i < chunks; i++) {File file = new File(uploadPath, i + "_" + fileName);// 并发情况,需要判断所有,因为可能最后一个分片传完,之前有的还没传完while (!file.exists()) {// 不存在休眠100毫秒后在重新判断Thread.sleep(100);}// 分片存在,读入数组中byte[] bytes = FileUtils.readFileToByteArray(file);os.write(bytes);os.flush();file.delete();}os.flush();}}}/*** 文件下载** @param request* @param response* @throws IOException*/public void download(HttpServletRequest request, HttpServletResponse response) throws IOException {// 获取文件File file = new File(downloadFile);// 获取下载文件信息DownloadFileInfo downloadFileInfo = getDownloadFileInfo(file.length(), request, response);// 设置响应头setResponse(response, file.getName(), downloadFileInfo);// 下载文件try (InputStream is = new BufferedInputStream(new FileInputStream(file));OutputStream os = new BufferedOutputStream(response.getOutputStream())) {// 跳过已经读取文件is.skip(downloadFileInfo.getPos());byte[] buffer = new byte[1024];long sum = 0;// 读取while (sum < downloadFileInfo.getRangeLength()) {int length = is.read(buffer, 0, (downloadFileInfo.getRangeLength() - sum) <= buffer.length ? (int) (downloadFileInfo.getRangeLength() - sum) : buffer.length);sum = sum + length;os.write(buffer, 0, length);}}}/*** 有两个map,我要去判断里面相同键的值一致不一致,除了双重for循环,有没有别的好办法*/private DownloadFileInfo getDownloadFileInfo(long fSize, HttpServletRequest request, HttpServletResponse response) {long pos = 0;long last = fSize - 1;// 判断前端是否需要分片下载if (request.getHeader(RANGE) != null) {response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);String numRange = request.getHeader(RANGE).replace("bytes=", "");String[] strRange = numRange.split("-");if (strRange.length == 2) {pos = Long.parseLong(strRange[0].trim());last = Long.parseLong(strRange[1].trim());// 若结束字节超出文件大小,取文件大小if (last > fSize - 1) {last = fSize - 1;}} else {// 若只给一个长度,开始位置一直到结束pos = Long.parseLong(numRange.replace("-", "").trim());}}long rangeLength = last - pos + 1;String contentRange = "bytes " + pos + "-" + last + "/" + fSize;return new DownloadFileInfo(fSize, pos, last, rangeLength, contentRange);}/*** 分片下载** @throws IOException*/public void downloads() throws IOException {File file = new File(downloadPath);// 如果文件夹不存在则创建if (!file.exists() && !file.isDirectory()) {file.mkdirs();}// 探测下载,获取文件相关信息FileInfo fileInfoDto = sliceDownload(1, 10, -1, null);// 如果不为空,执行分片下载if (fileInfoDto != null) {// 计算有多少分片long pages = fileInfoDto.getFileSize() / PER_SLICE;// 适配最后一个分片for (long i = 0; i <= pages; i++) {long start = i * PER_SLICE;long end = (i + 1) * PER_SLICE - 1;executorService.execute(new SliceDownloadRunnable(start, end, i, fileInfoDto.getFileName()));}}}/*** 分片下载** @param start 分片起始位置* @param end   分片结束位置* @param page  第几个分片, page=-1时是探测下载*/private FileInfo sliceDownload(long start, long end, long page, String fName) throws IOException {// 断点下载File file = new File(downloadPath, page + "-" + fName);// 如果当前文件已经存在,并且不是探测任务,并且文件的长度等于分片的大小,那么不用下载当前文件if (file.exists() && page != -1 && file.length() == PER_SLICE) {return null;}// 创建HttpClientHttpClient client = HttpClients.createDefault();HttpGet httpGet = new HttpGet("http://localhost:8018/file/download");httpGet.setHeader(RANGE, "bytes=" + start + "-" + end);HttpResponse httpResponse = client.execute(httpGet);String fSize = httpResponse.getFirstHeader("fSize").getValue();fName = URLDecoder.decode(httpResponse.getFirstHeader("fName").getValue(), UTF_8);HttpEntity entity = httpResponse.getEntity();// 下载try (InputStream is = entity.getContent();FileOutputStream fos = new FileOutputStream(file)) {byte[] buffer = new byte[1024];int ch;while ((ch = is.read(buffer)) != -1) {fos.write(buffer, 0, ch);}fos.flush();}// 判断是否是最后一个分片,如果是那么合并if (end - Long.parseLong(fSize) > 0) {mergeFile(fName, page);}return new FileInfo(Long.parseLong(fSize), fName);}private void mergeFile(String fName, long page) throws IOException {File file = new File(downloadPath, fName);try (BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file))) {for (int i = 0; i <= page; i++) {File tempFile = new File(downloadPath, i + "-" + fName);// 文件不存在或文件没写完while (!tempFile.exists() || (i != page && tempFile.length() < PER_SLICE)) {Thread.sleep(100);}byte[] bytes = FileUtils.readFileToByteArray(tempFile);os.write(bytes);os.flush();tempFile.delete();}// 删除文件File f = new File(downloadPath, "-1" + "-null");if (f.exists()) {f.delete();}} catch (InterruptedException e) {e.printStackTrace();}}private class SliceDownloadRunnable implements Runnable {private final long start;private final long end;private final long page;private final String fName;private SliceDownloadRunnable(long start, long end, long page, String fName) {this.start = start;this.end = end;this.page = page;this.fName = fName;}@Overridepublic void run() {try {sliceDownload(start, end, page, fName);} catch (IOException e) {e.printStackTrace();}}}/*** 设置响应头*/private void setResponse(HttpServletResponse response, String fileName, DownloadFileInfo downloadFileInfo) throws UnsupportedEncodingException {response.setCharacterEncoding(UTF_8);response.setContentType("application/x-download");response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, UTF_8));// 支持分片下载response.setHeader("Accept-Range", "bytes");response.setHeader("fSize", String.valueOf(downloadFileInfo.getFSize()));response.setHeader("fName", URLEncoder.encode(fileName, UTF_8));// range响应头response.setHeader("Content-Range", downloadFileInfo.getContentRange());response.setHeader("Content-Length", String.valueOf(downloadFileInfo.getRangeLength()));}}

5..编写上传文件实体类、文件信息实体类和下载文件实体类

package com.liyh.entity;import lombok.Data;@Data
public class UploadFileInfo {/*** 文件名称*/private String fileName;/*** 上传文件会有多个分片,记录当前为那个分片*/private Integer currentChunk;/*** 总分片数*/private Integer chunks;}
package com.liyh.entity;import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileInfo {private long fileSize;private String fileName;}
package com.liyh.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@AllArgsConstructor
@NoArgsConstructor
@Data
public class DownloadFileInfo {/*** 文件总大小*/private long fSize;/*** 断点起始位置*/private long pos;/*** 断点结束位置*/private long last;/*** rang响应*/private long rangeLength;/*** range响应*/private String contentRange;}

运行效果

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

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

相关文章

Spring AI探索

Spring AI概述 该Spring AI项目旨在简化包含人工智能功能的应用程序的开发&#xff0c;避免不必要的复杂性。 该项目从著名的 Python 项目&#xff08;例如 LangChain 和 LlamaIndex&#xff09;中汲取灵感&#xff0c;但 Spring AI 并非这些项目的直接移植。该项目的成立基于…

RabbitMq 延迟队列

前言 延迟消息队列在我们工作中使用的场景特别多&#xff0c;比如超时未支付取消订单&#xff0c;异步业务时间有时间间隔&#xff0c;等等&#xff0c;今天我们就来聊一聊使用消息延迟队列 需求 使用RabbitMq 实现延迟队列&#xff0c;5分钟之后进行消息的消费方式 基于死…

Qt画五角星,简单图表

五角星&#xff1a; 代码&#xff1a; widget.cpp #include "widget.h" #include "ui_widget.h" #include <QPaintEvent> #include <QPainter> #include <QPainterPath> Widget::Widget(QWidget *parent): QWidget(parent), ui(new U…

基于WPF技术的换热站智能监控系统13--控制设备开关

1、本节目的 本次工作量相对有点大&#xff0c;有点难度&#xff0c;需要熟悉MVVM模式&#xff0c;特别是属性绑定和命令驱动&#xff0c;目标是点击水泵开关&#xff0c;让风扇转动或停止&#xff0c;风扇连接的管道液体流动或静止。 &#xff0c;具体对应关系是&#xff1a;…

9.8k star!一款小而美的开源物联网操作系统:RT-Thread

介绍 RT-Thread是一款主要由中国开源社区主导开发的开源实时操作系统&#xff08;RTOS&#xff09;。它不仅是一个实时操作系统内核&#xff0c;也是一个完整的应用系统&#xff0c;包含了实时、嵌入式系统相关的各个组件&#xff0c;如TCP/IP协议栈、文件系统、libc接口、图形…

【算法专题--链表】删除排序链表中的重复元素 -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐双指针 四、总结与提炼 五、共勉 一、前言 删除排序链表中的重复元素这道题&#xff0c;可以说是--链表专题--&#xff0c;最经典的一道题&#xff0c;也是在面试中频率最高的一道题目&#xff0c;通常在面试中&#xff0…

记录一个flink跑kafka connector遇到的问题

【报错】 D:\Java\jdk1.8.0_231\bin\java.exe "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.3\lib\idea_rt.jar56647:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.3\bin" -Dfile.encodingUTF-8 -classpath D:\Java\jdk1.8.0_231\jre\lib\cha…

手机照片免费数据恢复软件EasyRecovery2024免费版下载

大家好&#xff01;今天我要给大家推荐一款非常棒的软件——EasyRecovery。相信大家都知道&#xff0c;电脑中的重要文件一旦丢失&#xff0c;对我们的工作和学习都会产生很大的影响。 而EasyRecovery软件就是专门解决这个问题的利器&#xff01;它能够帮助我们快速、有效地恢…

【CSS in Depth2精译】1.1 层叠

CSS 本质上就是声明规则&#xff0c;并让这些特定的规则在各种情况下生效。一个类添加到某个元素上&#xff0c;则应用这个类包含的这一些样式&#xff1b;元素 X 是元素 Y 的一个子节点&#xff0c;则应用另一些样式。浏览器于是根据这些规则&#xff0c;判定所有样式生效的具…

最长不下降子序列LIS详解

最长不下降子序列指的是在一个数字序列中&#xff0c;找到一个最长的子序列&#xff08;可以不连续&#xff09;&#xff0c;使得这个子序列是不下降&#xff08;非递减&#xff09;的。 假如&#xff0c;现有序列A[1&#xff0c;2&#xff0c;3&#xff0c;-1&#xff0c;-2&…

【Android】三种常见的布局LinearLayout、GridLayout、RelativeLayout

【Android】三种常见的布局LinearLayout、GridLayout、RelativeLayout 在 Android 开发中&#xff0c;布局&#xff08;Layout&#xff09;是构建用户界面的基础。通过合理的布局管理&#xff0c;可以确保应用在不同设备和屏幕尺寸上都能有良好的用户体验。本文将简单介绍 And…

Go微服务框架Kratos中makefile命令的使用方法及报错处理

运用 kratos 微服务框架开发项目时&#xff0c;可以使用提供的 makefile 中的命令自动且快速生产相关代码&#xff0c;提高开发效率。 krotos中makefile文件内容如下&#xff1a; GOHOSTOS:$(shell go env GOHOSTOS) GOPATH:$(shell go env GOPATH) VERSION$(shell git descri…

java程序什么时候需要在运行的时候动态修改字节码对象

一、java程序什么时候需要在运行的时候动态修改字节码对象 我认为有两种场景&#xff0c;一种是无法修改源代码的时候&#xff1b;另外一种是功能增强的时候。 1、无法修改源代码 举个例子&#xff0c;java程序依赖的第三方的jar包中发现了bug&#xff0c;但是官方还没有修复…

工程设计问题-步进锥滑轮问题

该问题的主要目标是用5个变量使4阶锥皮带轮的重量最小&#xff0c;其中4个变量是皮带轮每个台阶的直径&#xff0c;最后一个变量是滑轮的宽度。该问题包含11个非线性约束&#xff0c;以保证传动功率必须为0.75马力。 Abhishek Kumar, Guohua Wu, Mostafa Z. Ali, Rammohan Mall…

启动mysql 3.5时出现 MySql 服务正在启动 . MySql 服务无法启动。

有可能是端口冲突 netstat -ano | findstr :3306运行这段代码出现类似&#xff1a; 可以看到端口 3306 已经被进程 ID 为 6284 的进程占用。为了启动新的 MySQL 服务&#xff0c;我们需要停止这个进程或更改新服务的端口&#xff1a; 1、终止进程 taskkill /PID 6284 /F2、确…

【计算机毕业设计】基于Springboot的车辆管理系统【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…

贷款投资决策和常用财务函数

前段时间上了一门excel操作的课&#xff0c;本文结合其中介绍财务函数以及投资决策分析相关的部分&#xff0c;对贷款中的现金流计算进行深入的分析。 以等额本息产品为例进行实操计算&#xff0c;假设某产品本金12000元&#xff0c;期限12&#xff0c;IRR利率24%。每期还款113…

项目:双人五子棋对战-对战模块(6)

完整代码见: 邹锦辉个人所有代码: 测试仓库 - Gitee.com 当玩家进入到游戏房间后, 就要开始一局紧张而又刺激的五子棋对战了, 本文将就前端后端的落子与判断胜负的部分作详细讲解. 模块详细讲解 约定前后端交互的接口 首先是建立连接后, 服务器需要生成一些游戏的初始信息(可…

c语言——扫雷游戏(简易版)

目录 前言游戏设计 前言 什么是扫雷游戏&#xff1f; 游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子&#xff0c;同时避免踩雷&#xff0c;踩到一个雷即全盘皆输。 这个游戏对于c语言的初学者来说难度还是挺大的&#xff0c;那我就实现一个初学者也能快速学…

黄仁勋最新建议:找到一门技艺,用一生去完善、磨炼!

“你可能会找到你的英伟达。我希望你们将挫折视为新的机遇。” 黄仁勋职业生涯中最大的教诲并非来自导师或科技公司 CEO&#xff0c;而是来自他在国际旅行时遇到的一位园丁。 近日在加州理工学院毕业典礼上发表演讲时&#xff0c;黄仁勋向毕业生分享了自己在日本京都的小故事。…