logo 范 · 拾光录
网址收集 关于作者 Github Gitee
杂文随笔5
Hexo博客:基础使用Hexo博客:Next主题Hexo博客:Next进阶使用Hexo博客:Next高级配置基于Node的WIKI管理
前端知识16
HTML常用知识CSS常用知识CSS美化checkbox复选框JavaScript常用知识JavaScript格式化时间戳JavaScript窗口宽高处理JavaScript黑夜主题切换实现方案JavaScript数字转大写简易图片查看器TypeScript基础知识Threejs基础三要素Threejs网格辅助和轨道控制器Threejs物体绘制Electron基础使用Nodejs基础知识animate.css页面动画
Vue框架19
Vite的使用及扩展Vue3父子组件Vue3使用Marked解析MarkdownMermaid图表生成库初始化页面加载动画Axios表单提交二维码解决方案NProgress加载进度条Vue3动态菜单实现Vue3使用ECharts图表Vue3处理Excel导入导出keep-alive页面缓存及setup问题Element:文件上传Element:结合Pinia实现动态菜单Element:图片上传组件Element:自定义统一弹窗组件Element:表格自定义指令控制按钮显示(鉴权)可视化大屏使用缩放适配分辨率
UniApp15
UniApp的基础使用封装网络请求工具及文件上传uni-app的开发记录微信小程序分享原生文件上传Pinia取消滚动条(兼容小程序)tabbar消息数量显示scroll-view上滑到底部加载数据状态栏高度动态设配数据共享与传递uview-plus导航栏实现背景融合Wot UIWot UI实现顶部背景图融合uni-app x
Java基础知识10
基础知识面向对象Lambda表达式常用API常用知识积累try-with-resource注解反射多线程经纬度距离计算
SpringBoot31
application配置Maven创建聚合项目全局异常处理锁机制项目启动初始化数据方式邮件功能集成原生定时任务异步集成阿里云OSS阿里OSS预签名上传基于hutool读excelJSR303WebSocketWebSocket版AI接口流式调用Smart-Doc接口文档生成器application配置信息加密雪花算法工具AOP实现请求参数脱敏思路JWT生成Token及工具类SpringBoot默认JSON与对象转换若依框架:安装使用若依框架:优化和调整文件上传若依框架:管理后台页面优化若依框架:后端接口代码优化SpringAISpringBoot实现AI接口流式调用服务启动时创建MySQL连接自建项目工程树形结构处理工具微信支付代码微信手机号登录
SpringMVC14
跨域处理拦截器RESTful风格伪前后端分离Jackson转换器调整Thymeleaf基于拦截器做权限校验AOP打印接口请求响应日志AOP打印接口请求响应耗时文件上传和回显POST请求加解密实现(AES)POST请求加解密实现(RSA+AES)参数动态校验实现方案真实IP和归属地
MyBatis8
MyBatis基本使用与配置Mapper使用相关MaBatis多数据源配置MyBatisPlus数据统计类处理方案MyBatisPlus条件查询正向工程的实现(H2)mybatis-plus-join
SpringCloud15
Netflix:微服务与搭建Netflix:服务的消费与提供Netflix:EurekaNetflix:ActuatorNetflix:RibbonNetflix:FeignNetflix:HystrixNetflix:ZuulAlibaba:简介与搭建Alibaba:Nacos注册中心Alibaba:RibbonAlibaba:OpenFeignAlibaba:Nacos配置中心Alibaba:GetewayAlibaba:Sentinel
MySQL6
MySQL基础知识MySQL多表查询与事务MySQL常用函数及解决方案MySQL视图MySQL索引安装MySQL
Redis7
Redis介绍和安装Redis配置文件Redis持久化Redis集群Redis语法基础Redis相关问题及解决方案SpringBoot集成Redis使用记录
MongoDB10
Linux安装MongoDBMongoDB基础语法MongoTemplate及SpringBoot配置MongoTemplate中Update操作MongoTemplate中聚合查询MongoTemplate日期归档示例项目使用相关知识归纳地理位置存储与距离查询MongoDB副本集与事务获取类名和属性名工具类
其他数据库1
H2数据库
Python编程6
Python基础知识Python语法yolo目标检测OpenCV的使用及树莓派平台condauv
工具集合13
IDEAMavenGradleGitNginx安装Nginx配置VSCodeJMeter压测DockerOllamaRustFSPicGoObs录制
Linux知识11
Linux常用命令Jar启动脚本VirtualBox安装CentOSVirtualBox安装Ubuntu树莓派安装及使用frp内网穿透ArchLinux:基础系统安装ArchLInux:图形化界面安装ArchLinux:常用软件ArchLinux:深度优化ArchLinux:Niri
创意设计2
Blender:入门知识UI设计基础知识
AI相关9
Claude CodeHermes AgentOpenAI基本使用OpenAI工具调用OpenAI记忆管理OpenAI推理执行OpenAI开发框架Langchainllama.cpp

解决问题

适用于大文件上传,直接用户对接OSS,减少用户上传服务器、服务器再上传OSS的时间

依赖

<!--阿里OSS-->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.16.3</version>
</dependency>

Java代码

package com.fan.controller;

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.fan.vo.ComResult;
import lombok.Data;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

/**
 * 文件上传
 */
@RestController
public class FileController {

    /**
     * 预签名请求响应对象
     */
    @Data
    public static class preSignedUrlVo {
        private String fileName;
        private String preUrl;
        private String url;
    }

    /**
     * 生成预签名
     */
    @PostMapping("/manage/file/getPreSignedUrl")
    public ComResult<preSignedUrlVo> getPreSignedUrl(@RequestBody preSignedUrlVo params) {
        if (Objects.isNull(params.getFileName())) {
            return ComResult.failMsg("文件名不能为空");
        }
        // 获取文件后缀
        String suffix = params.getFileName().substring(params.getFileName().lastIndexOf("."));
        // 生成文件夹,按月份进行归类
        String folderName = new SimpleDateFormat("yyyyMM").format(new Date());
        // 生成文件名称
        String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        // 随机数字6位数字,多机模式
        String random = String.valueOf((int) (Math.random() * 1000000));
        // 地区
        String endpoint = "https://oss-cn-chengdu.aliyuncs.com";
        // Bucket所在地域,只要cn-xxx
        String region = "cn-chengdu";
        // accessKeyId
        String accessKeyId = "xxxxxxxxxxxxxx";
        // accessKeySecret
        String accessKeySecret = "xxxxxxxxxxxxxxx";
        // Bucket
        String bucketName = "fanmr";
        // 完整路径
        String objectName = "wws" + "/" + folderName + "/" + fileName + "_" + random + suffix;
        // 创建OSSClient实例
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        // 使用直接提供的 AK 构建 CredentialsProvider
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(CredentialsProviderFactory.newDefaultCredentialProvider(accessKeyId, accessKeySecret))
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();
        try {
            URL signedUrl;
            // 指定生成的预签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。
            Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
            // 生成预签名URL。
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.PUT);
            request.setContentType("application/octet-stream");
            // 设置过期时间。
            request.setExpiration(expiration);
            // 通过HTTP PUT请求生成预签名URL。
            signedUrl = ossClient.generatePresignedUrl(request);
            // 打印预签名URL
            System.out.println("文件上传预签名地址:" + signedUrl.toString());
            // 返回地址
            params.setPreUrl(signedUrl.toString());
            params.setUrl(endpoint.replace("//", "//" + bucketName + ".") + "/" + objectName);
            return ComResult.data(params);
        } catch (OSSException e) {
            System.out.println(e.getMessage());
        }
        return ComResult.failMsg("预签名失败");
    }

}

Vue配合ElementPlus

代码

FileOneUploadComponent.vue,注意需要axios

<!-- 单文件上传 -->
<!-- 注意修改上传接口、Token -->
<template>
  <div class="file-one-upload">
    <el-upload
      ref="upload"
      :headers="{ token: store.token }"
      :limit="1"
      :http-request="imageUploadBefore"
      :on-exceed="handleExceed">
      <el-button type="primary" :icon="Upload" :disabled="progressData.show">点击上传</el-button>
    </el-upload>
    <div class="progress" v-if="progressData.show">
      <el-progress :percentage="progressData.progress" />
    </div>
  </div>
</template>

<script setup>
import { ref, getCurrentInstance, watch, reactive } from 'vue'
// 注意store的位置,这里用的Pinia,注意名称
import { sysStore } from '../store'
import { Upload } from '@element-plus/icons-vue'
// 这里是多环境配置中的接口地址
let api = import.meta.env.env_http_api
// 名称需要与Pinia导出一致
const store = sysStore()

//
import http from '../http'
import axios from 'axios'
//

const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  }
})
const mv = ref(props.modelValue)
const { emit } = getCurrentInstance()
watch(
  () => props.modelValue,
  () => {
    mv.value = props.modelValue
  }
)

// 文件上传前
let fileSize = ref('')
// 进度条
const progressData = reactive({
  progress: 0,
  show: false
})
async function imageUploadBefore(option) {
  console.log(option)
  let file = option.file

  // 转换为MB
  let size = (file.size / 1024 / 1024).toFixed(2)
  fileSize = size + 'MB'
  emit('update:fileSize', fileSize)
  // 预签名
  http
    .post('/manage/file/getPreSignedUrl', {
      fileName: file.name
    })
    .then((res) => {
      if (res.data.code === 200) {
        // 发送PUT请求上传文件,只能用原生axios,不能携带多的参数
        progressData.progress = 0
        progressData.show = true
        axios
          .put(res.data.data.preUrl, file, {
            headers: {
              'Content-Type': 'application/octet-stream'
            },
            onUploadProgress: function (progressEvent) {
              // 对于大文件,可能需要计算百分比来显示上传进度
              if (progressEvent.total > 0) {
                const percentCompleted = Math.round(
                  (progressEvent.loaded * 100) / progressEvent.total
                )
                progressData.progress = percentCompleted
              }
            }
          })
          .then(() => {
            emit('update:modelValue', res.data.data.url)
          })
          .catch((error) => {
            console.error(error)
          })
          .finally(() => {
            progressData.show = false
          })
      }
    })
}

// 允许覆盖
const upload = ref()
const handleExceed = (files) => {
  upload.value.clearFiles()
  const file = files[0]
  file.uid = genFileId()
  upload.value.handleStart(file)
}
</script>

<style scoped lang="scss">
// 进度展示位置调整
.progress {
  margin: 8px 0 12px 0;
}

/* 隐藏上传列表,无效提到全局 */
.file-one-upload .el-upload-list {
  display: none !important;
}
</style>

使用

import FileOneUploadComponent from './FileOneUploadComponent.vue'

<FileOneUploadComponent
              v-model="modifyData.req.file"
              v-model:fileSize="modifyData.req.size" />
解决问题
依赖
Java代码
Vue配合ElementPlus
代码
使用