当前位置: 首页 > article >正文

java大视频分片上传

实现原理,前端控制每次上传1mb,后端接受1mb,并记录该分片下标,返回给前端还未上传的下标,直到所有的都上传完成

controller

@ApiOperation(value = "上传视频", notes = "上传视频", httpMethod = "POST", response = WebResult.class)
@PostMapping(value = "/uploadVideo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)//
public AjaxResult uploadVideo(@RequestParam(name = "file") MultipartFile file,
                              @RequestParam(name = "chunkIndex") Integer chunkIndex,
                              @RequestParam(name = "md5") String md5,
                              @RequestParam(name = "totalFileSize") Long totalFileSize,
                              @RequestParam(name = "fileName") String fileName,
                              @RequestParam(name = "userName") String userName,
                              HttpServletRequest request) throws Exception {
    if (null == file || file.isEmpty()) {
        return AjaxResult.error("文件内容不能为空!");
    }

    return fileSystemService.uploadVideo(file, chunkIndex, md5, fileName, userName);
    }

service

public AjaxResult uploadVideo(MultipartFile file, Integer chunkIndex, String md5, String fileName, String userName) throws IOException {
        //创建存放文件夹
        String date = DateTime.now().toString("yyyyMMdd");
        String dirPath = ConstantUtils.FILE_VIDEO_PATH + userName + "/" + date + "/" + md5 + "/";
        File dirFile = new File(dirPath);
        if (!dirFile.exists()){
            dirFile.mkdirs();
        }
        //分区文件
        String relativeFilePath = dirPath + fileName;
        File tempFile = new File(relativeFilePath);
        RandomAccessFile rw = new RandomAccessFile(tempFile, "rw");
        //定位到分片的偏移量
        rw.seek(Long.parseLong(ConstantUtils.FILE_VIDEO_CHUNK_SIZE) * chunkIndex);
        //写入分片数据
        rw.write(file.getBytes());
        //关闭流
        rw.close();
        //读取已经分片的集合
        Set<Object> hasChunkList = new HashSet<>();
        String hasChunkKey = ConstantUtils.CHUNK_PREFIX + md5;
        if (redisHelper.hasKey(hasChunkKey)){
            Object o = redisHelper.get(hasChunkKey);
            JSONArray array = JSONUtil.parseArray(o);
            hasChunkList.addAll(array);
        }
        hasChunkList.add(chunkIndex);
        //最新分片下标跟新到redis
        redisHelper.set(hasChunkKey,hasChunkList);

        HashMap<String, Object> map = new HashMap<>();
        map.put("url",userName + "/" + date + "/" + md5 + "/" + fileName);
        map.put("hasList",hasChunkList);
        return AjaxResult.success(map);
    }

ConstantUtils

package com.ruoyi.file.utils;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


/**
 * @author csb
 * @description: 获取配置文件常量数据
 * @date 2023/9/20
 */

// spring初始化bean的时候,如果bean实现了InitializingBean接口,
// 会自动调用afterPropertiesSet方法
@Component
public class ConstantUtils implements InitializingBean {

    @Value("${file.video.videoPath}")
    String videoPath;

    @Value("${file.video.chunkSize}")
    String videoChunkSize;


    public static String FILE_VIDEO_PATH;

    public static String FILE_VIDEO_CHUNK_SIZE;

    public static String CHUNK_PREFIX = "FILE_VIDEO";

    @Override
    public void afterPropertiesSet() throws Exception {
        FILE_VIDEO_PATH = videoPath;
        FILE_VIDEO_CHUNK_SIZE = videoChunkSize;
    }
}

RedisHelper

package com.ruoyi.file.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class RedisHelper {

    private static final Logger logger = LoggerFactory.getLogger(RedisHelper.class);

    public static final int CACHE_TIME_1_YEAR = 60 * 60 * 24 * 365;


    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public Object get(final String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 写入缓存
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().set(key, value);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 写入缓存
     */
    public boolean set(final String key, Object value, int seconds) {
        boolean result = false;
        try {
            if (seconds > 0) {
                redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    public boolean exist(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 更新缓存
     */
    public boolean getAndSet(final String key, Object value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().getAndSet(key, value);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 删除缓存
     */
    public boolean delete(final String key) {
        boolean result = false;
        try {
            redisTemplate.delete(key);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 1.Redis设置多个值
     * 命令:HMSET myhash total 15 success 0 time "2019-12-01 11:10:15"
     * 2.当一个任务完成之后,把成功的次数加1
     * 命令:HINCRBY myhash success 1
     * <p>
     * 当前有多少个计算(有点过几次计算)
     * 1.列表中添加值
     * 命令:RPUSH mylist "hello"
     * 2.获取列表中所有元素
     * 命令:LRANGE mylist 0 -1
     */

    /**
     * 设置hash值,同时设置多个属性
     *
     * @param key 键
     * @param map 多个属性值用map封装
     * @return
     */
    public boolean hmset(final String key, Map<String, Object> map) {
        boolean result = false;
        try {
            HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();
            opsForHash.putAll(key, map);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 自增值,给hash值某个属性自增
     *
     * @param key   键
     * @param field 要自增的属性
     * @param num   自增值的大小,可以为正数负数
     * @return
     */
    public boolean hincrby(final String key, String field, Integer num) {
        boolean result = false;
        try {
            HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();
            opsForHash.increment(key, field, num);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 自增值,给hash值某个属性自增1
     *
     * @param key   键
     * @param field 要自增的属性
     * @return
     */
    public boolean hincrby(final String key, String field) {
        boolean result = false;
        try {
            HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();
            opsForHash.increment(key, field, 1);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 获取hash中的所有数据
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hgetall(final String key) {
        Map<Object, Object> entries = new HashMap<>();
        try {
            HashOperations<Object, Object, Object> opsForHash = redisTemplate.opsForHash();
            entries = opsForHash.entries(key);
            return entries;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return entries;
    }

    /**
     * list操作,队列右侧添加值
     *
     * @param key
     * @param value
     * @return
     */
    public boolean rpush(final String key, Object value) {
        boolean result = false;
        try {
            ListOperations<Object, Object> opsForList = redisTemplate.opsForList();
            opsForList.rightPush(key, value);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 获取列表中的所有元素
     *
     * @param key
     * @return
     */
    public List<Object> lrange(final String key) {
        List<Object> range = new ArrayList<>();
        try {
            ListOperations<Object, Object> opsForList = redisTemplate.opsForList();
            range = opsForList.range(key, 0, -1);
            return range;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return range;
    }

    /**
     * 删除list中的值
     *
     * @param key   list的key
     * @param value 要删除的list中的value
     * @return
     */
    public boolean lrem(final String key, Object value) {
        boolean result = false;
        try {
            ListOperations<Object, Object> opsForList = redisTemplate.opsForList();
            opsForList.remove(key, 0, value);
            result = true;
        } catch (Exception e) {
            logger.warn("", e);
        }
        return result;
    }

    /**
     * 设置键的过期时间
     * @param key 键
     * @param expiredTimeSecond 过期时间(秒)
     * @return
     */
    public boolean setKeyExpiredTime(String key, Long expiredTimeSecond){
        return this.redisTemplate.expire(key, expiredTimeSecond,TimeUnit.SECONDS);
    }

}


http://www.kler.cn/a/411320.html

相关文章:

  • GCP Dataproc有什么特点,有什么最佳实践
  • 如何选择黑白相机和彩色相机
  • 【Python爬虫实战】深入解析 Scrapy:从阻塞与非阻塞到高效爬取的实战指南
  • 一个高度可扩展的 Golang ORM 库【GORM】
  • 速盾:CDN缓存的工作原理是什么?
  • 非root用户安装CUDA
  • 【Conda 】Conda 配置文件详解:优化你的包管理与环境设置
  • 【Nginx】核心概念与安装配置解释
  • Docker login 报证书存储错误的解决办法
  • (完整版Word原件)智慧产业园区能源管控系统解决方案,能源管理系统解决方案-能源数字化监控解决方案,工业能源管理系统解决方案,园区能源管理
  • 探索Python网页解析新纪元:requests-html库揭秘
  • [C++]深入剖析list类中迭代器的封装
  • HOW - React 状态模块化管理和按需加载(一) - react-redux
  • 【Python中while循环】
  • Spring Boot 整合 ELK 全面指南:实现日志采集、分析与可视化
  • java——利用 Tomcat 自定义的类加载器实现热加载
  • 最长回文子串&多/虚继承
  • 网络安全原理与技术思考题/简答题
  • 1126刷题
  • 堆的实现(完全注释版本)
  • pikachu文件上传漏洞通关详解
  • 利用 Vue 组合式 API 与 requestAnimationFrame 优化大量元素渲染
  • Paddle Inference部署推理(一)
  • QT-installEventFilter
  • GaussDB高智能--库内AI引擎:模型管理数据集管理
  • 蓝桥杯c++算法秒杀【6】之动态规划【下】(数字三角形、砝码称重(背包问题)、括号序列、异或三角:::非常典型的必刷例题!!!)