java视频上传,播放预览,解决苹果手机视频无法预览的问题

需求:上传视频后,点击链接可以在PC和手机端播放预览,包括苹果手机

1.相关工具类

1.1文件上传工具类
package resources.util;


import common.util.FileOperateUtil;
import common.util.HttpClientUtil;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.util.DateUtil;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;

/**
 * @description:
 * @projectName:alumni
 * @see:resources.util
 * @author:
 * @createTime:2021/11/25 13:41
 * @version:1.0
 */
public class FileUploadTool {

    TransfMediaTool transfMediaTool = new TransfMediaTool();
    // 文件最大500M
    private static long upload_maxsize = 800 * 1024 * 1024;
    // 文件允许格式
    private static String[] allowFiles = {".rar", ".doc", ".docx", ".zip",
            ".pdf", ".txt", ".swf", ".xlsx", ".gif", ".png", ".jpg", ".jpeg",
            ".bmp", ".xls", ".mp4", ".flv", ".ppt", ".avi", ".mpg", ".wmv",
            ".3gp", ".mov", ".asf", ".asx", ".vob", ".wmv9", ".rm", ".rmvb"};
    // 允许转码的视频格式(ffmpeg)
    private static String[] allowFLV = {".avi", ".mpg", ".wmv", ".3gp",
            ".mov", ".asf", ".asx", ".vob"};
    // 允许的视频转码格式(mencoder)
    private static String[] allowAVI = {".wmv9", ".rm", ".rmvb"};

    public JSONObject createFile(MultipartFile multipartFile, HttpServletRequest request, String staticRes, String from, String userId, String orgCode) {
        boolean bflag = false;
        String fileName = multipartFile.getOriginalFilename().toString();
        fileName = FileOperateUtil.produceFileName(fileName);//生成新的文件名
        // 判断文件不为空
        if (multipartFile.getSize() != 0 && !multipartFile.isEmpty()) {
            bflag = true;
            // 判断文件大小
            if (multipartFile.getSize() <= upload_maxsize) {
                bflag = true;
                // 文件类型判断
                if (this.checkFileType(fileName)) {
                    bflag = true;
                } else {
                    bflag = false;
                    System.out.println("文件类型不允许");
                }
            } else {
                bflag = false;
                System.out.println("文件大小超范围");
            }
        } else {
            bflag = false;
            System.out.println("文件为空");
        }
        if (bflag) {
            String logoPathDir = staticRes + "/video/" + orgCode + "/" + from + "/" + userId + "/" + DateUtil.formatDate(new Date(), "yyyy-MM-dd" );
            //String logoRealPathDir = request.getSession().getServletContext().getRealPath(logoPathDir);
            File logoSaveFile = new File(logoPathDir);
            if (!logoSaveFile.exists()) {
                logoSaveFile.mkdirs();
            }
            String name = fileName.substring(0, fileName.lastIndexOf("."));
            System.out.println("文件名称:" + name);
            // 新的文件名
            String newFileName = this.getName(fileName);
            // 文件扩展名
            String fileEnd = this.getFileExt(fileName);
            String fileNamedirs = logoPathDir + File.separator + newFileName + fileEnd;
            System.out.println("保存的路径:" + fileNamedirs);
            File filedirs = new File(fileNamedirs);//依据路径创建的文件
            // 转入文件
            try {
                FileOperateUtil.inputCopy(multipartFile.getInputStream(), filedirs);
               // multipartFile.transferTo(filedirs);
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 相对路径
            String fileDir = logoPathDir + newFileName + fileEnd;
            //String fileDir = logoPathDir + newFileName;
            StringBuilder builder = new StringBuilder(fileDir);
            String finalFileDir = builder.substring(1);
            // size存储为String
            String size = this.getSize(filedirs);
            // 源文件保存路径
            String aviPath = logoSaveFile.getAbsolutePath();
                JSONObject result = new JSONObject();
                result.put("fileName", newFileName+fileEnd);//新文件名
                result.put("size", size);//大小
                result.put("contentType", HttpClientUtil.getMimeType(filedirs.getName()));//文件标识
                String localUrl = request.getContextPath() + "/down/video/" + orgCode + "/" + from + "/" + userId + "/" + DateUtil.formatDate(new Date(), "yyyy-MM-dd") + "/" + fileName;
                result.put("localUrl", localUrl);//路径
                return result;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 文件类型判断
     *
     * @param fileName
     * @return
     */
    private boolean checkFileType(String fileName) {
        Iterator<String> type = Arrays.asList(allowFiles).iterator();
        while (type.hasNext()) {
            String ext = type.next();
            if (fileName.toLowerCase().endsWith(ext)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 视频类型判断(flv)
     *
     * @param
     * @return
     */
    private boolean checkMediaType(String fileEnd) {
        Iterator<String> type = Arrays.asList(allowFLV).iterator();
        while (type.hasNext()) {
            String ext = type.next();
            if (fileEnd.equals(ext)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 视频类型判断(AVI)
     *
     * @param
     * @return
     */
    private boolean checkAVIType(String fileEnd) {
        Iterator<String> type = Arrays.asList(allowAVI).iterator();
        while (type.hasNext()) {
            String ext = type.next();
            if (fileEnd.equals(ext)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件扩展名
     *
     * @return string
     */
    private String getFileExt(String fileName) {
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 依据原始文件名生成新文件名
     *
     * @return
     */
    private String getName(String fileName) {
        Iterator<String> type = Arrays.asList(allowFiles).iterator();
        while (type.hasNext()) {
            String ext = type.next();
            if (fileName.contains(ext)) {
                String newFileName = fileName.substring(0, fileName.lastIndexOf(ext));
                return newFileName;
            }
        }
        return "";
    }

    /**
     * 文件大小,返回kb.mb
     *
     * @return
     */
    private String getSize(File file) {
        String size = "";
        long fileLength = file.length();
        DecimalFormat df = new DecimalFormat("#.00");
        if (fileLength < 1024) {
            size = df.format((double) fileLength) + "BT";
        } else if (fileLength < 1048576) {
            size = df.format((double) fileLength / 1024) + "KB";
        } else if (fileLength < 1073741824) {
            size = df.format((double) fileLength / 1048576) + "MB";
        } else {
            size = df.format((double) fileLength / 1073741824) + "GB";
        }
        return size;
    }
}

2.视频上传代码的实现

/**
     * description 视频上传
     * param [request, file, from, userId, orgCode]
     * return common.enums.ResultVO
     * author 
     * createTime 2021/11/26 11:36
     **/
    @PostMapping("/video")
    public ResultVO uploadVideo(HttpServletRequest request, MultipartFile file, String from, String userId, String orgCode) {
        String message = "";
        JSONObject entity = null;
        // FileEntity entity = new FileEntity();
        FileUploadTool fileUploadTool = new FileUploadTool();
        ModelMap map = new ModelMap();
        try {
            entity = fileUploadTool.createFile(file, request, staticRes, from, userId, orgCode);
            if (entity != null) {
//                service.saveFile(entity);
                message = "上传成功";
                map.put("entity", entity);
                map.put("result", message);
            } else {
                message = "上传失败";
                map.put("result", message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ResultVO(ResultCode.SUCCESS, entity);
    }

3.视频下载PC播放以及手机播放的实现

 /**
     * description 视频下载器
     * param [request, response]
     * return common.enums.ResultVO
     * author 
     * createTime 2021/11/26 11:39
     **/
    @GetMapping("/video/**")
    public ResultVO downVideo(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String fileUrl = request.getRequestURI();
        try {
            fileUrl = URLDecoder.decode(fileUrl, "UTF-8");
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        fileUrl = fileUrl.substring(fileUrl.indexOf("/video") + 6);
        String imgPath = staticRes + "/video" + fileUrl;
        String realPath = imgPath;
        File tmFile = new File(realPath);
        if ((!tmFile.exists()) || tmFile.length() < 1) {
            throw new RuntimeException("文件不存在!");
        }
        try {

			/*response.setContentType("video/mp4");
			InputStream ins = FileOperateUtil.inputStreamFromFile(tmFile);
			OutputStream ous = response.getOutputStream();
			FileOperateUtil.readToOutStream(ins, ous, null);*/
            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
            sendVideo(request, response, tmFile, fileName);

        } catch (IOException e) {
            e.printStackTrace();
        }

        return new ResultVO(ResultCode.SUCCESS, "下载成功");
    }

    //解决苹果 使用 video 标签不能正常播放视频问题
    //刚开始时代码返回的视频流是在一个请求里全部返回的,而苹果的浏览器会先发一次探测请求来获取文件大小,之后再发送多次请求来分段取数据流的数据,其实这里就是一个分段上传的思想(Accept-Ranges)。有两个很重要的点就是,
    //第一:需要根据请求内容的不同做出不同的响应,第一次探测请求需要返回200,后面的请求需要返回206和具体数据
    //第二:contentType必须设置为video/mp4。
    private void sendVideo(HttpServletRequest request, HttpServletResponse response, File file, String fileName) throws FileNotFoundException, IOException {
        RandomAccessFile randomFile = new RandomAccessFile(file, "r");//只读模式
        long contentLength = randomFile.length();
        String range = request.getHeader("Range");
        int start = 0, end = 0;
        if (range != null && range.startsWith("bytes=")) {
            String[] values = range.split("=")[1].split("-");
            start = Integer.parseInt(values[0]);
            if (values.length > 1) {
                end = Integer.parseInt(values[1]);
            }
        }
        int requestSize = 0;
        if (end != 0 && end > start) {
            requestSize = end - start + 1;
        } else {
            requestSize = Integer.MAX_VALUE;
        }

        byte[] buffer = new byte[4096];
        response.setContentType("video/mp4");
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("ETag", fileName);
        response.setHeader("Last-Modified", new Date().toString());
        //第一次请求只返回content length来让客户端请求多次实际数据
        if (range == null) {
            response.setHeader("Content-length", contentLength + "");
        } else {
            //以后的多次以断点续传的方式来返回视频数据
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);//206
            long requestStart = 0, requestEnd = 0;
            String[] ranges = range.split("=");
            if (ranges.length > 1) {
                String[] rangeDatas = ranges[1].split("-");
                requestStart = Integer.parseInt(rangeDatas[0]);
                if (rangeDatas.length > 1) {
                    requestEnd = Integer.parseInt(rangeDatas[1]);
                }
            }
            long length = 0;
            if (requestEnd > 0) {
                length = requestEnd - requestStart + 1;
                response.setHeader("Content-length", "" + length);
                response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength);
            } else {
                length = contentLength - requestStart;
                response.setHeader("Content-length", "" + length);
                response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1) + "/" + contentLength);
            }
        }
        ServletOutputStream out = response.getOutputStream();
        int needSize = requestSize;
        randomFile.seek(start);
        while (needSize > 0) {
            int len = randomFile.read(buffer);
            if (needSize < buffer.length) {
                out.write(buffer, 0, needSize);
            } else {
                out.write(buffer, 0, len);
                if (len < buffer.length) {
                    break;
                }
            }
            needSize -= buffer.length;
        }
        randomFile.close();
        out.close();

    }

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值