网站建设系统有哪些/沈阳今日新闻头条
一、上传文件基本实现
- 1、前端效果图展示,这里使用
element-ui plus
来展示样式效果
-
2、基础代码如下
<template><div><el-uploadref="uploadRef"class="upload-demo":limit="1":on-change="handleExceed":auto-upload="false"><template #trigger><el-button type="primary">选择文件</el-button></template><el-button style="margin-left: 30px" type="success" @click="submitUpload"> 上传 </el-button></el-upload></div> </template><script setup>import { ref } from 'vue';import axios from 'axios';const fileRef = ref(null);const handleExceed = (files) => {console.log(files);fileRef.value = files;};const submitUpload = () => {console.log('开始上传文件', JSON.stringify(fileRef.value));const formData = new FormData();formData.append('file', fileRef.value.raw);axios.post('http://localhost:9002/file/upload', formData).then((res) => {console.log(fileRef.value, '??');console.log('上传成功');});}; </script><style lang="scss" scoped></style>
-
3、定义后端接口,并且处理好跨域(关于跨域处理,自己百度处理)
package com.course.file.controller;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;@RestController public class UploadController {private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);@PostMapping("/upload")public String uploadApi(@RequestParam MultipartFile file) {this.LOG.info("上传文件开始");this.LOG.info(file.getOriginalFilename());this.LOG.info(String.valueOf(file.getSize()));return "上传成功";} }
-
4、保存文件到本地文件
package com.course.file.controller;import com.course.file.utils.UuidUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;import java.io.File; import java.io.IOException;@RestController public class UploadController {private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);@PostMapping("/upload")public String uploadApi(@RequestParam MultipartFile file) throws IOException {this.LOG.info("上传文件开始");this.LOG.info(file.getOriginalFilename());this.LOG.info(String.valueOf(file.getSize()));// 保存文件到本地String fileName = file.getOriginalFilename();String key = UuidUtil.getShortUuid();// 需要先本地创建一个file文件夹String fullPath = "E:/file/" + key + "-" + fileName;File dest = new File(fullPath);file.transferTo(dest);return "上传成功";} }
二、配置静态目录
-
1、在
FileApplication.java
旁边添加一个SpringMvcConfig.java
的文件package com.course.file;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class SpringMvcConfig implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/f/**").addResourceLocations("file:E:/file/");}// http://localhost:9002/file/f/FdXMQJdF-xx.png }
-
2、直接浏览器上直接访问上面的地址
-
3、上传地址返回给前端
@RestController public class UploadController {private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);@PostMapping("/upload")public String uploadApi(@RequestParam MultipartFile file) throws IOException {this.LOG.info("上传文件开始");this.LOG.info(file.getOriginalFilename());this.LOG.info(String.valueOf(file.getSize()));// 保存文件到本地String fileName = file.getOriginalFilename();String key = UuidUtil.getShortUuid();// 需要先本地创建一个file文件夹String fullPath = "E:/file/" + key + "-" + fileName;File dest = new File(fullPath);file.transferTo(dest);return "http://localhost:9002/file/f/" + key + "-" + fileName;} }
-
4、将上面几个固定的配置写到配置文件中
file.path=E:/file/ file.domain=http://localhost:9002/file/ # 修改上传文件大小(不设置大文件可能上传失败) spring.servlet.multipart.max-file-size=50MB spring.servlet.multipart.max-request-size=50MB
-
5、在控制器中使用
public class UploadController {private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);@Value("${file.path}")private String FILE_PATH;@Value("${file.domain}")private String FILE_DOMAIN; }
三、断点续传
-
1、主要原理
- 前端将大文件根据文件大小来切割成小片段,使用递归的方式调用后端接口,将文件上传到服务器端
- 服务器端接收到前端上传片段,存储到服务器上
- 等前端最后一个上传完成后,将全部的文件合并成一个文件
- 合并完成后,返回一个
url
地址,将之前的分片上传的文件删除
-
2、手动演示前端分段上传
const submitUpload = () => {console.log('开始上传文件', JSON.stringify(fileRef.value));const formData = new FormData();const file = fileRef.value.raw;// 文件分配let shardSize = 5 * 1024 * 1024; // 以5MB为一个分片let shardIndex = 0; // 分片索引let start = shardIndex * shardSize; // 开始位置let end = Math.min(file.size, start + shardSize); // 结束位置let fileShard = file.slice(start, end); // 每次上传的分片数据formData.append('file', fileShard);axios.post('http://localhost:9002/file/upload', formData).then((res) => {console.log(fileRef.value, '??');console.log('上传成功');});};
-
3、第二次的时候将
shardIndex
改为1
-
4、查看本地文件夹下的文件
-
5、手动创建一个接口来尝试合并文件
@GetMapping("merge")public String merge() throws FileNotFoundException {// 最终合成后的视频文件名称File newFile = new File(FILE_PATH + "test.mp4");FileOutputStream outputStream = new FileOutputStream(newFile, true);FileInputStream fileInputStream = null;byte[] bytes = new byte[5 * 1024 * 1024];int len;try {// 读取第一段fileInputStream = new FileInputStream(new File(FILE_PATH + "/pN0EoOny-blob"));while ((len = fileInputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, len);}// 读取第二段fileInputStream = new FileInputStream(new File(FILE_PATH + "/f5oeIEDW-blob"));while ((len = fileInputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, len);}// 读取第三段fileInputStream = new FileInputStream(new File(FILE_PATH + "/qsm8n03q-blob"));while ((len = fileInputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, len);}} catch (IOException e) {LOG.error("合并分片失败", e);} finally {try {if (fileInputStream != null) {fileInputStream.close();}outputStream.close();LOG.info("IO流关闭");} catch (IOException e) {LOG.error("IO流关闭", e);}}return "合并视频成功";}
四、使用数据库来实现分片上传
-
1、数据表字段
在数据库中涉及
key
只跟文件有关,跟上传多少片没关系的,当已经上传的分片数和分片总数一样的时候就合并文件drop table if exists `file`; create table `file` (`id` int(11) not null PRIMARY key auto_increment comment '主键id',`path` varchar(100) not null comment '相对路径',`name` varchar(100) comment '文件名',`suffix` varchar(10) comment '后缀',`size` int(11) comment '大小|字节B',`shard_index` int(11) DEFAULT 0 COMMENT '已上传分片',`shard_size` int(11) DEFAULT 0 COMMENT '分片大小',`shard_total` int(11) DEFAULT 0 COMMENT '分片总数',`key` VARCHAR(100) DEFAULT NULL COMMENT '文件标识',`created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',`updated_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',`deleted_at` timestamp(6) NULL DEFAULT NULL COMMENT '软删除时间' ) engine=innodb default charset=utf8mb4 comment='文件';
-
2、在
pom.xml
文件中添加mybatis-plus
的依赖包<!-- 配置连接到数据库 --> <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.26</version> </dependency> <!-- mybatis plus 依赖包 --> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version> </dependency><!-- mybatis 代码生成器 --> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.2.0</version> </dependency>
-
3、
application.properties
添加配置# mysql数据库链接 spring.datasource.url=jdbc:mysql://localhost:3306/beego?characterEncoding=utf-8&serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=123456# 配置mybatis-plus # 开启下划线转驼峰 mybatis-plus.configuration.map-underscore-to-camel-case=true mybatis-plus.configuration.auto-mapping-behavior=full mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # mapping的路径 mybatis-plus.mapper-locations=classpath*:mapper/**/*Mapper.xml #主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID"; mybatis-plus.global-config.db-config.id-type=AUTO # 逻辑删除(软删除) mybatis-plus.global-config.db-config.logic-delete-value=NOW() mybatis-plus.global-config.db-config.logic-not-delete-value=NULL
-
4、使用模板生成器生成代码
-
5、前端使用递归的方式来分片上传文件
<script setup>import { ref } from 'vue';import { genFileId } from 'element-plus';import axios from 'axios';import md5 from 'js-md5';const fileRef = ref(null);const handleExceed = (files) => {console.log(files);fileRef.value = files;};const updateFile = (shardIndex) => {const formData = new FormData();const file = fileRef.value.raw;// 文件分配let shardSize = 5 * 1024 * 1024; // 以5MB为一个分片// let shardIndex = 0; // 分片索引let start = (shardIndex - 1) * shardSize; // 开始位置let end = Math.min(file.size, start + shardSize); // 结束位置let fileShard = file.slice(start, end); // 每次上传的分片数据// 前端多上传参数let size = file.size;let shardTotal = Math.ceil(size / shardSize); // 总片数const suffix = file.name.substring(file.name.lastIndexOf('.') + 1);formData.append('shard', fileShard);formData.append('shardIndex', shardIndex);formData.append('shardSize', shardSize);formData.append('shardTotal', shardTotal);formData.append('name', file.name);formData.append('size', size);formData.append('suffix', suffix); formData.append('key', md5(`${file.name}_${file.size}_${file.type}`));axios.post('http://localhost:9002/file/upload1', formData).then((res) => {// 判断如果当前的shardIndex < shardTotal的时候递归上传if (shardIndex < shardTotal) {updateFile(++shardIndex);} else {console.log('上传成功');}});};const submitUpload = () => {console.log('开始上传文件', JSON.stringify(fileRef.value));// 开始上传文件updateFile(1);}; </script>
-
6、后端对文件上传处理,存储到本地和入库操作
package com.course.file.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.course.file.model.FileEntity; import com.course.file.service.IFileService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;import java.io.File; import java.io.IOException;@RestController public class FileController {@Autowiredprivate IFileService fileService;private static final Logger LOG = LoggerFactory.getLogger(FileController.class);@Value("${file.path}")private String FILE_PATH;@Value("${file.domain}")private String FILE_DOMAIN;@PostMapping("upload1")public String upload1(@RequestParam MultipartFile shard,Integer shardIndex,Integer shardSize,Integer shardTotal,String name,String suffix,Integer size,String key) throws IOException {this.LOG.info("开始上传文件");System.out.println("当前分片:" + shardIndex);System.out.println("当前分片大小:" + shardSize);System.out.println("当前分片总数:" + shardTotal);System.out.println("文件名称:" + name);System.out.println("文件后缀名:" + suffix);System.out.println("文件大小:" + size);System.out.println("文件唯一的key:" + key);// 文件保存到本地目录下String localPath = this.FILE_PATH + key + "." + suffix + "." + shardIndex;File dest = new File(localPath);shard.transferTo(dest);LOG.info(dest.getAbsolutePath());// 数据入库操作LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FileEntity::getFileKey, key);FileEntity fileEntity = new FileEntity();if (this.fileService.getOne(queryWrapper) != null) {// 说明不是第一次上传,需要更新当前上传的分片数量fileEntity.setShardIndex(shardIndex);if (this.fileService.update(fileEntity, queryWrapper)) {return "上传成功";} else {return "上传失败";}} else {// 第一次上传创建String path = this.FILE_PATH + key + "." + suffix;fileEntity.setFileKey(key);fileEntity.setName(name);fileEntity.setPath(path);fileEntity.setShardIndex(shardIndex);fileEntity.setSuffix(suffix);fileEntity.setShardSize(shardSize);fileEntity.setShardTotal(shardTotal);fileEntity.setSize(size);if (this.fileService.save(fileEntity)) {return "上传成功";} else {return "上传失败";}}} }
-
7、查看本地目录是否生成分片文件
-
8、对本地分片文件合并操作,当
shardIndex=shardTotal
的时候进行合并操作@RestController public class FileController {@Autowiredprivate IFileService fileService;private static final Logger LOG = LoggerFactory.getLogger(FileController.class);@Value("${file.path}")private String FILE_PATH;@Value("${file.domain}")private String FILE_DOMAIN;@PostMapping("upload1")public String upload1(@RequestParam MultipartFile shard,Integer shardIndex,Integer shardSize,Integer shardTotal,String name,String suffix,Integer size,String key) throws IOException {this.LOG.info("开始上传文件");System.out.println("当前分片:" + shardIndex);System.out.println("当前分片大小:" + shardSize);System.out.println("当前分片总数:" + shardTotal);System.out.println("文件名称:" + name);System.out.println("文件后缀名:" + suffix);System.out.println("文件大小:" + size);System.out.println("文件唯一的key:" + key);// 文件保存到本地目录下String localPath = this.FILE_PATH + key + "." + suffix + "." + shardIndex;File dest = new File(localPath);shard.transferTo(dest);LOG.info(dest.getAbsolutePath());// 合并文件操作if (Objects.equals(shardIndex, shardTotal)) {this.merge(key, suffix, shardTotal);}// 数据入库操作LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FileEntity::getFileKey, key);FileEntity fileEntity = new FileEntity();if (this.fileService.getOne(queryWrapper) != null) {// 说明不是第一次上传,需要更新当前上传的分片数量fileEntity.setShardIndex(shardIndex);if (this.fileService.update(fileEntity, queryWrapper)) {return "上传成功";} else {return "上传失败";}} else {// 第一次上传创建String path = this.FILE_PATH + key + "." + suffix;fileEntity.setFileKey(key);fileEntity.setName(name);fileEntity.setPath(path);fileEntity.setShardIndex(shardIndex);fileEntity.setSuffix(suffix);fileEntity.setShardSize(shardSize);fileEntity.setShardTotal(shardTotal);fileEntity.setSize(size);if (this.fileService.save(fileEntity)) {return "上传成功";} else {return "上传失败";}}}/*** 对上传的文件片段合并操作* @param key* @param suffix* @param shardTotal* @throws FileNotFoundException*/private void merge(String key, String suffix, Integer shardTotal) throws FileNotFoundException {this.LOG.info("=====开始合并切片操作=====");String path = this.FILE_PATH + key + "." + suffix;File newFile = new File(path);FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加写入FileInputStream fileInputStream = null;//分片文件byte[] byt = new byte[10 * 1024 * 1024];int len;try {for (int i = 0; i < shardTotal; i++) {fileInputStream = new FileInputStream(new File(this.FILE_PATH + key + "." + suffix + "." + (i + 1)));while ((len = fileInputStream.read(byt)) != -1) {outputStream.write(byt, 0, len);}}} catch (Exception e) {this.LOG.error("分片合并异常", e);} finally {try {if (fileInputStream != null) {fileInputStream.close();}outputStream.close();LOG.info("IO流关闭");} catch (Exception e) {LOG.error("IO流关闭", e);}}this.LOG.info("=====结束合并切片操作=====");} }
-
9、当合并后可以对之前的分片进行删除操作,避免占用更的磁盘空间
private void merge(String key, String suffix, Integer shardTotal) throws FileNotFoundException, InterruptedException {// ...this.LOG.info("=====结束合并切片操作=====");System.gc();Thread.sleep(1000);// 删除分片LOG.info("删除分片开始");for (int i = 0; i < shardTotal; i++) {String filePath = path + "." + (i + 1);File file = new File(filePath);boolean result = file.delete();this.LOG.info("删除{},{}", filePath, result ? "成功" : "失败");}LOG.info("删除分片结束"); }
五、前端使用BS64
提交数据
-
1、使用
bs64
提交后端可以直接定义一个字符串接收数据,将接收到的数据转换为图片 -
2、前端将上传的文件转换为
bs64
// 使用BS64上传const bs64UploadHandler = () => {const file = fileRef.value.raw;let fileReader = new FileReader();fileReader.onload = function (e) {const base64 = e.target.result;console.log('base64', base64);const suffix = file.name.substring(file.name.lastIndexOf('.') + 1);axios.post('http://localhost:9002/file/bs64Upload', {fileName: md5(`${file.name}_${file.size}_${file.type}`) + '.' + suffix,fileBs64: base64,}).then((res) => {console.log(res);});};fileReader.readAsDataURL(file);};
-
3、后端定义一个方法,将字符串转换为
MultipartFile
数据类型package com.course.file.utils;import org.springframework.web.multipart.MultipartFile; import sun.misc.BASE64Decoder;import java.io.*;public class Base64ToMultipartFile implements MultipartFile {private final byte[] imgContent;private final String header;public Base64ToMultipartFile(byte[] imgContent, String header) {this.imgContent = imgContent;this.header = header.split(";")[0];}@Overridepublic String getName() {// TODO - implementation depends on your requirementsreturn System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];}@Overridepublic String getOriginalFilename() {// TODO - implementation depends on your requirementsreturn System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];}@Overridepublic String getContentType() {// TODO - implementation depends on your requirementsreturn header.split(":")[1];}@Overridepublic boolean isEmpty() {return imgContent == null || imgContent.length == 0;}@Overridepublic long getSize() {return imgContent.length;}@Overridepublic byte[] getBytes() throws IOException {return imgContent;}@Overridepublic InputStream getInputStream() throws IOException {return new ByteArrayInputStream(imgContent);}@Overridepublic void transferTo(File dest) throws IOException, IllegalStateException {new FileOutputStream(dest).write(imgContent);}public static MultipartFile base64ToMultipart(String base64) {try {String[] baseStrs = base64.split(",");BASE64Decoder decoder = new BASE64Decoder();byte[] b = new byte[0];b = decoder.decodeBuffer(baseStrs[1]);for(int i = 0; i < b.length; ++i) {if (b[i] < 0) {b[i] += 256;}}return new Base64ToMultipartFile(b, baseStrs[0]);} catch (IOException e) {e.printStackTrace();return null;}} }
-
4、直接保存文件,这里就不做分片上传
@PostMapping("bs64Upload") public String bs64Upload(@RequestBody FileDTO req) throws IOException {// 1.将上传的bs64转为图片String bs64 = req.getFileBs64();MultipartFile shard = Base64ToMultipartFile.base64ToMultipart(bs64);// 图片保存到本地String localPath = this.FILE_PATH + req.getFileName() ;File dest = new File(localPath);shard.transferTo(dest);LOG.info(dest.getAbsolutePath());return this.FILE_DOMAIN + req.getFileName(); }
六、断点续传
-
1、断点续传主要原谅,前端在点击上传按钮的时候先调用后端一个接口,判断之前是否有上传过记录,如果有就返回之前上传的分片
shardIndex
,前端就继续以这个分片来上传,如果没有就返回0
表示从0
开始上传 -
2、后端定义一个接口根据
key
来查询数据库是否已经有上传过记录@GetMapping("{key}") public Integer getShardIndexByKeyApi(@PathVariable String key) {LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(FileEntity::getFileKey, key).select(FileEntity::getShardIndex);FileEntity fileEntity = this.fileService.getOne(queryWrapper);return fileEntity.getShardIndex(); }
-
3、前端在使用上传前先调用上面的接口
const submitUpload = () => {console.log('开始上传文件', JSON.stringify(fileRef.value));const file = fileRef.value.raw;const key = md5(`${file.name}_${file.size}_${file.type}`);axios.get(`http://localhost:9002/file/${key}`).then((response) => {if (response.data > 0) {// 历史上传updateFile(response.data + 1);} else {// 首次上传updateFile(1);}});};
七、秒传功能
- 1、当前端使用
MD5
加密后提交的名字在数据库已经存在,则直接拼接url
返回就可以,不需要再次上传
八、上传到阿里OSS
-
1、配置依赖包
<!-- 阿里云oss存储 --> <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.10.2</version> </dependency>
-
2、封装方法使用
import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.model.ObjectMetadata; import com.aliyun.oss.model.PutObjectResult; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile;import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Date; import java.util.List; import java.util.UUID;/*** 阿里云上传文件*/ @Component public class OssUtil {@Value("${aliyun.oss.endpoint}")private String endpoint;@Value("${aliyun.oss.accessKeyId}")private String accessKeyId;@Value("${aliyun.oss.accessKeySecret}")private String accessKeySecret;@Value("${aliyun.oss.bucketName}")private String bucketName;//文件存储目录(自定义阿里云的)private String fileDir = "clouFile/";/*** 单个文件上传** @param file* @return*/public String uploadFile(MultipartFile file) {// 调用封装好的上传文件方法String fileUrl = uploadImg2Oss(file);// 返回完整的路径String str = getFileUrl(fileUrl);return str.trim();}/*** 单个文件上传(指定文件名需要后缀名)** @param file* @param fileName* @return*/public String uploadFile(MultipartFile file, String fileName) {try {InputStream inputStream = file.getInputStream();this.uploadFile2OSS(inputStream, fileName);return fileName;} catch (Exception e) {return "上传失败";}}/*** 多个文件的上传,返回路径用,分割** @param fileList* @return*/public String uploadFile(List<MultipartFile> fileList) {String fileUrl = "";String str = "";String photoUrl = "";for (int i = 0; i < fileList.size(); i++) {fileUrl = uploadImg2Oss(fileList.get(i));str = getFileUrl(fileUrl);if (i == 0) {photoUrl = str;} else {photoUrl += "," + str;}}return photoUrl.trim();}/*** 获取完整的路径名** @param fileUrl* @return*/private String getFileUrl(String fileUrl) {if (fileUrl != null && fileUrl.length() > 0) {String[] split = fileUrl.split("/");String url = this.getUrl(this.fileDir + split[split.length - 1]);return url;}return null;}/*** 获取去掉参数的完整路径** @param url* @return*/private String getShortUrl(String url) {String[] imgUrls = url.split("\\?");return imgUrls[0].trim();}/*** 获取url地址** @param key* @return*/private String getUrl(String key) {// 设置URL过期时间为20年 3600l* 1000*24*365*20Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 20);// 生成URLOSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);if (url != null) {return getShortUrl(url.toString());}return null;}private String uploadImg2Oss(MultipartFile file) {//1、限制最大文件为20Mif (file.getSize() > 1024 * 1024 * 20) {return "图片太大";}//2、重命名文件String fileName = file.getOriginalFilename();String suffix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(); //文件后缀String uuid = UUID.randomUUID().toString();String name = uuid + suffix;try {InputStream inputStream = file.getInputStream();this.uploadFile2OSS(inputStream, name);return name;} catch (Exception e) {return "上传失败";}}/*** 使用阿里云上传文件** @param inStream 输入流* @param fileName 文件名称* @return*/private String uploadFile2OSS(InputStream inStream, String fileName) {String ret = "";try {//创建上传Object的MetadataObjectMetadata objectMetadata = new ObjectMetadata();objectMetadata.setContentLength(inStream.available());objectMetadata.setCacheControl("no-cache");objectMetadata.setHeader("Pragma", "no-cache");objectMetadata.setContentType(getContentType(fileName.substring(fileName.lastIndexOf("."))));objectMetadata.setContentDisposition("inline;filename=" + fileName);//上传文件OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);PutObjectResult putResult = ossClient.putObject(bucketName, fileDir + fileName, inStream, objectMetadata);ret = putResult.getETag();} catch (IOException e) {System.out.println(e.getMessage());} finally {try {if (inStream != null) {inStream.close();}} catch (IOException e) {e.printStackTrace();}}return ret;}/*** 获取文件类型** @param FilenameExtension* @return*/private static String getContentType(String FilenameExtension) {if (FilenameExtension.equalsIgnoreCase(".bmp")) {return "image/bmp";}if (FilenameExtension.equalsIgnoreCase(".gif")) {return "image/gif";}if (FilenameExtension.equalsIgnoreCase(".jpeg") ||FilenameExtension.equalsIgnoreCase(".jpg") ||FilenameExtension.equalsIgnoreCase(".png")) {return "image/jpeg";}if (FilenameExtension.equalsIgnoreCase(".html")) {return "text/html";}if (FilenameExtension.equalsIgnoreCase(".txt")) {return "text/plain";}if (FilenameExtension.equalsIgnoreCase(".vsd")) {return "application/vnd.visio";}if (FilenameExtension.equalsIgnoreCase(".pptx") ||FilenameExtension.equalsIgnoreCase(".ppt")) {return "application/vnd.ms-powerpoint";}if (FilenameExtension.equalsIgnoreCase(".docx") ||FilenameExtension.equalsIgnoreCase(".doc")) {return "application/msword";}if (FilenameExtension.equalsIgnoreCase(".xml")) {return "text/xml";}//PDFif (FilenameExtension.equalsIgnoreCase(".pdf")) {return "application/pdf";}return "image/jpeg";} }
相关文章:

spring-boot中实现分片上传文件
一、上传文件基本实现 1、前端效果图展示,这里使用element-ui plus来展示样式效果 2、基础代码如下 <template><div><el-uploadref"uploadRef"class"upload-demo":limit"1":on-change"handleExceed":auto-…...

【ICN综述】信息中心网络隐私安全
ICN基本原理: 信息中心网络也是需要实现在不可信环境下可靠的信息交换和身份认证 信息中心网络采用以数据内容为中心的传输方式代替现有IP 网络中以主机为中心的通信方式,淡化信息数据物理或逻辑位置的重要性,以内容标识为代表实现数据的查找…...

基于STC12C5A60S2系列1T 8051单片机EEPROM应用
基于STC12C5A60S2系列1T 8051单片机EEPROM应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍STC12C5A60S2系列1T 8051单片机EEPROM介绍基于STC12C5A60S2系列1T 8051单…...

手撕排序之直接选择排序
前言: 直接选择排序是排序中比较简单的排序,同时也是时间复杂度不是很优的排序。 思想: 本文主要讲解直接选择排序的优化版本。 我们经过一次遍历直接将该数列中最大的和最小的值挑选出来,如果是升序,就将最小的和…...

洛谷 P1359 租用游艇
题目链接 P1359 租用游艇 普及 题目描述 长江游艇俱乐部在长江上设置了 n n n 个游艇出租站 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n,游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i i i 到游艇出租站…...

springboot中没有主清单属性解决办法
在执行一个 spring boot 启动类时,提示 没有主清单属性 一般这个问题是没加 spring-boot-maven-plugin 插件的问题,但是项目中已经加了 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifa…...

C/C++ static关键字详解(最全解析,static是什么,static如何使用,static的常考面试题)
目录 一、前言 二、static关键字是什么? 三、static关键字修饰的对象是什么? 四、C 语言中的 static 🍎static的C用法 🍉static的重点概念 🍐static修饰局部变量 💦static在修饰局部变量和函数的作用 &a…...

windwos10搭建我的世界服务器,并通过内网穿透实现联机游戏Minecraft
文章目录 1. Java环境搭建2.安装我的世界Minecraft服务3. 启动我的世界服务4.局域网测试连接我的世界服务器5. 安装cpolar内网穿透6. 创建隧道映射内网端口7. 测试公网远程联机8. 配置固定TCP端口地址8.1 保留一个固定tcp地址8.2 配置固定tcp地址 9. 使用固定公网地址远程联机 …...

【实战Flask API项目指南】之七 用JWT进行用户认证与授权
实战Flask API项目指南之 用JWT进行用户认证与授权 本系列文章将带你深入探索实战Flask API项目指南,通过跟随小菜的学习之旅,你将逐步掌握 Flask 在实际项目中的应用。让我们一起踏上这个精彩的学习之旅吧! 前言 当小菜踏入Flask后端开发…...

鸿蒙LiteOs读源码教程+向LiteOS中添加一个简单的基于线程运行时的短作业优先调度策略
【⭐据说点赞收藏的都会收获好运哦👍】 一、鸿蒙Liteos读源码教程 鸿蒙的源码是放在openharmony文件夹下,openharmony下的kernel文件夹存放操作系统内核的相关代码和实现。 内核是操作系统的核心部分,所以像负责:资源管理、任…...

axios的使用与封装详细教程
目录 一、axios使用方式二、axios在main.js配置 一、axios使用方式 在 Spring Boot Vue 的项目中使用 Axios,你需要在 Vue 项目中安装 Axios 库,因为 Axios 是一个前端 JavaScript 库,用于发送 HTTP 请求和处理响应数据,而与 Sp…...

C++二叉搜索树
本章主要是二叉树的进阶部分,学习搜索二叉树可以更好理解后面的map和set的特性。 1.二叉搜索树概念 二叉搜索树的递归定义为:非空左子树所有元素都小于根节点的值,非空右子树所有元素都大于根节点的值,而左右子树也是二叉搜索树…...

elasticsearch索引按日期拆分
1.索引拆分原因 如果单个索引数据量过大会导致搜索变慢,而且不方便清理历史数据。 例如日志数据每天量很大,而且需要定期清理以往日志数据。例如原索引为sc_all_system_log,现按天拆分索引sc_all_system_log20220902,sc_all_syste…...

纯python实现大漠图色功能
大漠图色是一种自动化测试工具,可以用于识别屏幕上的图像并执行相应的操作。在Python中,可以使用第三方库pyautogui来实现大漠图色功能。具体步骤如下: 安装pyautogui库:在命令行中输入pip install pyautogui。导入pyautogui库&a…...

debounce and throtlle
debounce // 核心:单位时间内触发>1 则只执行最后一次。//excutioner 可以认为是执行器。执行器存在则清空,再赋值新的执行器。function debounce(fn, delay 500) {let excutioner null;return function () {let context this;let args arguments…...

四、数据库系统
数据库系统(Database System),是由数据库及其管理软件组成的系统。数据库系统是为适应数据处理的需要而发展起来的一种较为理想的数据处理系统,也是一个为实际可运行的存储、维护和应用系统提供数据的软件系统,是存储介…...

Linux中的高级IO
文章目录 1.IO1.1基本介绍1.2基础io的低效性1.3如何提高IO效率1.4五种IO模型1.5非阻塞模式的设置 2.IO多路转接之Select2.1函数的基本了解2.2fd_set理解2.3完整例子代码(会在代码中进行讲解)2.4优缺点 3.多路转接之poll3.1poll函数的介绍3.2poll服务器3.…...

项目管理之如何估算项目工作成本
在项目管理中,如何估算项目工作成本是一个关键问题。为了解决这个问题,我们可以采用自上而下的成本限额估算法和自下而上的成本汇总估算法。这两种方法各有优缺点,但都可以帮助我们准确地估算项目工作成本。 自上而下的成本限额估算法 自上…...

Redis主从复制基础概念
Redis主从复制:提高数据可用性和性能的策略 一、概述 Redis主从复制是一种常用的高可用性策略,通过将数据从一个Redis服务器复制到另一个或多个Redis服务器上,以提高数据的可用性和读取性能。当主服务器出现故障时,可以快速地切…...

图数据库Neo4j概念、应用场景、安装及CQL的使用
一、图数据库概念 引用Seth Godin的说法,企业需要摒弃仅仅收集数据点的做法,开始着手建立数据之间的关联关系。数据点之间的关系甚至比单个点本身更为重要。 传统的**关系数据库管理系统(RDBMS)**并不擅长处理数据之间的关系,那些表状数据模…...

路由器基础(四): RIP原理与配置
路由信息协议 (Routing Information Protocol,RIP) 是最早使用的距离矢量路由协议。因为路由是以矢量(距离、方向)的方式被通告出去的,这里的距离是根据度量来决定的,所以叫“距离矢量”。 距离矢量路由算法是动态路由算法。它的工作流程是:…...

红外遥控开发RK3568-PWM-IR
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言1.红外遥控的发送接收工作原理2.红外协议3.红外遥控系统框图4.遥控器添加方法4.1 记录键值4.2 添加键值总结前言 提示:这里可以添加本文要记录的大概内容: 1.红外遥控的发送接收工作原理 …...

go-sync-mutex
Sync Go 语言作为一个原生支持用户态进程(Goroutine)的语言,当提到并发编程、多线程编程时,往往都离不开锁这一概念。锁是一种并发编程中的同步原语(Synchronization Primitives),它能保证多…...

高并发系统设计
高并发系统通用设计方法 Scala-out 横向扩展,分散流量,分布式集群部署 缺点:引入复杂度,节点之间状态维护,节点扩展(上下线) Scala-up 提升单机性能,比如增加内存,增…...

Vue3-Pinia快速入门
1.安装pinia npm install pinia -save 2.在main.js中导入并使用pinia // 导入piniaimport { createPinia } from "pinia"; const pinia createPinia();//使用pinia app.use(pinia)app.mount(#app) 3.在src目录下创建包:store,表示仓库 4…...

Python算法——插入排序
插入排序(Insertion Sort)是一种简单但有效的排序算法,它的基本思想是将数组分成已排序和未排序两部分,然后逐一将未排序部分的元素插入到已排序部分的正确位置。插入排序通常比冒泡排序和选择排序更高效,特别适用于对…...

Java21新特性
目录 一、Java21新特性 1、字符串模版 2、scoped values 3、record pattern 4、switch格式匹配 5、可以在switch中使用when 6、Unnamed Classes and Instance Main Methods 7、Structured Concurrency 一、Java21新特性 1、字符串模版 字符串模版可以让开发者更简洁的…...

4 Tensorflow图像识别模型——数据预处理
上一篇:3 tensorflow构建模型详解-CSDN博客 本篇开始介绍识别猫狗图片的模型,内容较多,会分为多个章节介绍。模型构建还是和之前一样的流程: 数据集准备数据预处理创建模型设置损失函数和优化器训练模型 本篇先介绍数据集准备&am…...

SpringBoot整合RabbitMQ学习笔记
SpringBoot整合RabbitMQ学习笔记 以下三种类型的消息,生产者和消费者需各自启动一个服务,模拟生产者服务发送消息,消费者服务监听消息,分布式开发。 一 Fanout类型信息 . RabbitMQ创建交换机和队列 在RabbitMQ控制台,新…...

在校园跑腿系统小程序中,如何设计高效的实时通知与消息推送系统?
1. 选择合适的消息推送服务 在校园跑腿系统小程序中,选择一个适合的消息推送服务。例如,使用WebSocket技术、Firebase Cloud Messaging (FCM)、或第三方推送服务如Pusher或OneSignal等。注册并获取相关的API密钥或访问令牌。 2. 集成服务到小程序后端…...