使用 webuploader 大文件上传

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>File Upload</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/webuploader/0.1.1/webuploader.min.js"></script>
</head>
<body>
<div id="uploader">
    <div class="btns">
        <div id="picker">选择文件</div>
    </div>
</div>
<script>
    var uploader = WebUploader.create({
        auto: true,
        fileVal: "file",//文件上传域的name
        swf: 'https://cdn.bootcdn.net/ajax/libs/webuploader/0.1.1/Uploader.swf',
        server: 'http://localhost:8080/system/file/chunk',
        chunked: true,
        chunkSize: 10 * 1024 * 1024,
        duplicate: true,
        chunkRetry: 3,
        // threads: 10,
        pick: '#picker',
        method: 'POST', // 文件上传方式,POST或者GET。
        fileNumLimit: 1,//可上传文件数量
        fileSizeLimit: 100 * 1024 * 1024, //验证文件总大小是否超出限制, 超出则不允许加入队列。
        fileSingleSizeLimit: 50 * 1024 * 1024, //验证单个文件大小是否超出限制, 超出则不允许加入队列。

    });
   
    uploader.on('uploadComplete', function (file) {
        console.log('upload complete'); // 每个文件上传成功后,处理相关的事情
        console.log(file);
        $.ajax({
            url: 'http://localhost:8080/system/file/merge',
            data: {
                name: file.name
            },
            type: 'POST',
            success: function (data) {
                console.log(data);
            },
            error: function (data) {
                console.log(data);
            }
        });
    });

</script>
</body>
</html>

package com.question.controller;


import com.question.common.exception.ServerException;
import com.question.common.result.ResData;
import com.question.common.result.ResEnum;
import com.question.common.result.WangEditorImage;
import com.question.common.result.WangEditorVideo;
import com.question.utils.upload.FileUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.Map;

/**
 * @author kuaiting
 */

@RestController
@RequestMapping("/system/file")
public class FileController {

    @Value("${file.local}")
    private boolean local;

    @Value("${file.path}")
    private String path;


    @PostMapping("/chunk")
    public ResData<?> uploadChunk(@RequestParam("file") MultipartFile file,
                                  Integer chunk,
                                  Integer chunks,
                                  @RequestParam("name") String name, HttpServletRequest request) {
        String url = "";
        if (local && chunks!= null && chunks > 1) {
            url = FileUtils.uploadChunkFileToLocal(file, name, chunk, chunks, path, request);
        } else if (local) {
            url = FileUtils.uploadFileToLocal(file, request, path);
        } else {
            //上传到OSS
        }

        return ResData.success(ResEnum.SUCCESS.getCode(), "文件分片上传成功", url);

    }

    @PostMapping("/merge")
    public ResData<?> mergeChunks(@RequestParam("name") String name,
                              HttpServletRequest request) {
        String url =FileUtils.mergeChunks(name,path,request);
        return ResData.success(ResEnum.SUCCESS.getCode(), "文件分片上传成功", url);
    }


    /**
     * 文件上传
     *
     * @param file
     * @param request
     * @return
     * @throws ServerException
     */
    @PostMapping("/upload")
    public ResData<?> upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws ServerException {
        String url = null;
        if (local) {
            url = FileUtils.uploadFileToLocal(file, request, path);
        }
        return ResData.success(url);
    }

    /**
     * 下载文件
     *
     * @param name
     * @param open
     * @param response
     * @throws ServerException
     */
    @GetMapping("/download")
    public void download(@RequestParam String name, @RequestParam int open, HttpServletResponse response) throws ServerException {
        if (local) {
            FileUtils.downloadLocalFile(name, open, response, path);
        }
    }

    /**
     * 删除文件
     *
     * @param url
     * @return
     * @throws ServerException
     */
    @PostMapping("/delete")
    public ResData<String> delete(@RequestParam String url) throws ServerException {
        FileUtils.deleteFile(url);
        return ResData.success();
    }

    /**
     * wangeditor编辑器上传图片文件
     *
     * @param file
     * @param request
     * @return
     * @throws ServerException
     */
    @PostMapping("/wangeditorImage")
    public WangEditorImage wangeditorImage(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws ServerException {
        String url = "";
        if (local) {
            url = FileUtils.uploadFileToLocal(file, request, path);
        }
        String name = url.substring(url.lastIndexOf("/"));
        Map<String, Object> data = new HashMap<>();
        data.put("url", url);
        data.put("alt", name);
        data.put("href", url);
        return new WangEditorImage(0, ResEnum.SUCCESS.getMsg(), data);
    }


    /**
     * wangeditor编辑器上传视频文件
     *
     * @param file
     * @param request
     * @return
     * @throws ServerException
     */
    @PostMapping("/wangeditorVideo")
    public WangEditorVideo wangeditorVideo(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws ServerException {
        String url = null;
        if (local) {
            url = FileUtils.uploadFileToLocal(file, request, path);
        }
        Map<String, Object> data = new HashMap<>();
        data.put("url", url);
        data.put("poster", "");
        return new WangEditorVideo(0, ResEnum.SUCCESS.getMsg(), data);
    }


}


FileUtils

package com.question.utils.upload;

import com.question.common.exception.ServerException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;


/**
 * @author kuaiting
 */

@Component
public class FileUtils {

    static final String BASE_PATH = System.getProperty("user.dir") + "/src/main/resources/static/upload/";

    public static String uploadFileToLocal(MultipartFile multipartFile, HttpServletRequest request, String path) throws ServerException {
        String originalFilename = multipartFile.getOriginalFilename();
        String fileExtension = "";

        if (originalFilename.lastIndexOf(".") != -1) {
            fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }

        // 生成新的文件名,避免使用用户原始文件名
        String newFilename = originalFilename;
        File file =new File(path, newFilename);
        if (path != null && !path.isEmpty() ) {
            if( file.exists()){
                newFilename=   System.currentTimeMillis() + fileExtension;
                file = new File(path, newFilename);
            }else {
                file = new File(path, originalFilename);

            }

        } else {
            if( file.exists()){
                newFilename=   System.currentTimeMillis() + fileExtension;
                file = new File(BASE_PATH, newFilename);
            }else {
                file = new File(BASE_PATH, originalFilename);

            }
        }
        // 确保文件不存在
        if (file.exists()) {
            throw new ServerException("文件已存在");
        }
        // 确保目录存在
        File directory = file.getParentFile();
        if (directory != null && !directory.exists()) {
            directory.mkdirs();
        }

        //  大文件上传,使用缓冲流进行读写操作,避免内存溢出


        // 使用try-with-resources自动关闭资源
        try (InputStream fis = multipartFile.getInputStream();
             FileOutputStream fos = new FileOutputStream(file)) {
            // 创建一个更大的缓冲区,例如4096或8192字节
            byte[] buffer = new byte[8192]; // 创建一个更大的缓冲区
            int bytesRead;

            // 读取并写入文件
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }

            // 使用 request 生成访问URL
            String contextPath = request.getContextPath();
            String scheme = request.getScheme();
            String serverName = request.getServerName();
            int serverPort = request.getServerPort();
            String portSuffix = (serverPort == 80 || serverPort == 443) ? "" : ":" + serverPort;
            String basePath = scheme + "://" + serverName + portSuffix + contextPath + "/"+ "system" +  "/" + "file" + "/" + "download";
            return basePath + "?name=" + newFilename + "&open=1";
        } catch (IOException e) {
            throw new ServerException("上传失败");
        }
    }


    // 下载文件
    public static void downloadLocalFile(@RequestParam String name,
                                         int open,
                                         HttpServletResponse response, String path) throws ServerException {
        // 校验文件名,防止路径遍历攻击
        if (name.contains("..")) {
            throw new ServerException("不合法的文件名");
        }

        // 判断是否需要以附件形式打开文件
        String openStyle = open == 1 ? "inline" : "attachment";


        File file = null;
        if (path != null && !path.isEmpty()) {
            file = new File(path, name);
        } else {
            file = new File(BASE_PATH, name);
        }
        if (!file.exists()) {
            throw new ServerException("文件未找到");
        }
        // 使用 try-with-resources 自动关闭资源
        try (InputStream inputStream = new FileInputStream(file);
             OutputStream outputStream = response.getOutputStream()) {
            // 设置响应头部信息
            response.setHeader("Content-Disposition", openStyle + ";filename=" + name);

            // 循环读取文件内容,并将其写入输出流中
            byte[] buffer = new byte[4096]; // 创建一个更大的缓冲区
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush(); // 确保所有数据都被写出
        } catch (FileNotFoundException e) {
            throw new ServerException("文件未找到");
        } catch (IOException e) {
            // 记录更详细的错误信息
            throw new ServerException("下载失败");
        }
    }


    public static void deleteFile(String url) {
        try {
            String[] split = url.split("name=");
            File file = new File(BASE_PATH + split[1]);
            if (file.exists()) {
                file.delete();
            }
        } catch (Exception e) {
            throw new ServerException("删除失败");
        }
    }


    // 返回值:true表示是图片类型,false表示不是
    private static boolean isImageExtension(String extension) {
        return ".jpg".equalsIgnoreCase(extension) ||
                ".jpeg".equalsIgnoreCase(extension) ||
                ".png".equalsIgnoreCase(extension) ||
                ".gif".equalsIgnoreCase(extension) ||
                ".bmp".equalsIgnoreCase(extension);
    }


    // 返回值:true表示是视频类型,false表示不是
    private static boolean isVideoExtension(String extension) {

        return ".mp4".equalsIgnoreCase(extension) ||
                ".avi".equalsIgnoreCase(extension) ||
                ".mov".equalsIgnoreCase(extension) ||
                ".wmv".equalsIgnoreCase(extension) ||
                ".mkv".equalsIgnoreCase(extension);
    }

    public static String uploadChunkFileToLocal(MultipartFile file, String fileName, int chunk, int chunks, String path, HttpServletRequest request) {
        try {
            // 保存分片文件
            String uploadDir;
            if (path != null && !path.isEmpty()) {
                uploadDir = path;
            } else {
                uploadDir = BASE_PATH;
            }
            String chunkFileName = uploadDir + fileName + "_" + chunk + ".part";
            File chunkFile = new File(chunkFileName);
            // 如果文件不存在,则创建并写入内容  也间接实现断点续传的功能
            if (!chunkFile.exists()) {
                chunkFile.createNewFile();

                try (FileOutputStream fos = new FileOutputStream(chunkFile);
                     InputStream fis = file.getInputStream()) {
                    // 创建一个更大的缓冲区,例如4096或8192字节
                    byte[] buffer = new byte[8192]; // 创建一个更大的缓冲区
                    int bytesRead;

                    // 读取并写入文件
                    while ((bytesRead = fis.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }

                } catch (IOException e) {
                    throw new ServerException("上传失败");
                }
            }

            return null;
        } catch (IOException e) {
            throw new ServerException("上传失败");
        }
    }

    public static String mergeChunks(String fileName, String path, HttpServletRequest request) {
        try {
            // 文件重命名,生成新的文件名
            String extension = fileName.substring(fileName.lastIndexOf("."));
            String newFileName = System.currentTimeMillis() + extension;
            String uploadDir;
            if (path != null && !path.isEmpty()) {
                uploadDir = path;
            } else {
                uploadDir = BASE_PATH;
            }
            // 合并分片文件
            File mergeFile = new File(uploadDir, newFileName);
            // 如果文件不存在,则创建并写入内容
            if (!mergeFile.exists()) {
                mergeFile.createNewFile();
            }
            // 读取分片文件
            String[] chunkNames = new File(uploadDir).list((dir, name) -> name.startsWith(fileName) && name.endsWith(".part"));
            if (chunkNames == null || chunkNames.length == 0) {
                return null;
            }
            // 按顺序读取分片文件内容并写入合并文件
            try (FileOutputStream fos = new FileOutputStream(mergeFile, true)) {
                for (String chunkName : chunkNames) {
                    File chunkFile = new File(uploadDir + chunkName);
                    try (FileInputStream fis = new FileInputStream(chunkFile)) {
                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = fis.read(buffer)) != -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                    }
                    chunkFile.delete(); // 删除分片文件
                }
            }
            // 返回合并文件URL
            String contextPath = request.getContextPath();
            String scheme = request.getScheme();
            String serverName = request.getServerName();
            int serverPort = request.getServerPort();
            String portSuffix = (serverPort == 80 || serverPort == 443) ? "" : ":" + serverPort;
            String basePath = scheme + "://" + serverName + portSuffix + contextPath +"/" + "system" + "/" + "file" + "/" + "download";
            return basePath + "?name=" + newFileName + "&open=1";
        } catch (Exception e) {
            throw new ServerException("合并失败");
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

缘不易

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值