SpringBoot 文件上传下载 word转pdf 功能实现

720 阅读3分钟

Demo介绍

这个功能是为了能够以最小组件的方式展示如果使用SpringBoot实上传下载和相关预览功能,由于要测试后端接口,因此也写了较少的前端代码来测试接口,这里有较多省略,大家可以去项目那里运行

源代码在gitee.com/zrpNUDT/fil…

1 后端

后端的代码分成数个功能块

1.1 工具函数 Utils

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import org.springframework.core.io.ClassPathResource;

import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;

1.1.1 获取文件后缀名

要想实现对各个文件的操作,得知道文件的后缀名,代码如下

public static String getFileSuffix(String fileName){
    String fileSuffix;
    if(fileName.lastIndexOf(".")==-1){fileSuffix="";}
    else{fileSuffix=fileName.substring(fileName.lastIndexOf(".")+1);}
    return fileSuffix;
}

1.1.2 文件名转URL字符串

因为后端要对文件进行重定向,所以得对中文空格等字符进行转换

    public static String getUrlEncode(String fileName){
        String fileNameEncode;
        try {
            fileNameEncode = URLEncoder.encode(fileName,"UTF-8");
            //替换+号否则空格解析会出错            
            fileNameEncode = fileNameEncode.replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            fileNameEncode = "";
        }
        return fileNameEncode;
    }

1.1.3 word转pdf

采用的是apose.words的API 水印得有License才能去,这个暂时没做到,而且这个是收费的,商业项目慎用哦。



public static void WordToPdf(String sourcePath,String targetPath) throws Exception {
    FileOutputStream os = null;
    Boolean license = false;
    try {
        System.out.println(sourcePath+" -----> "+targetPath);
        //凭证 不然切换后有水印
        try{
            InputStream is = new ClassPathResource("static/license/license.xml").getInputStream();
            License aposeLic = new License();
            aposeLic.setLicense(is);
            license = true;
        }
        catch(Exception e){
            if (!license) {
                System.out.println("License验证不通过...");
            }
        }
        //生成一个空的PDF文件
        File file = new File(targetPath);
        os = new FileOutputStream(file);
        //要转换的word文件
        Document doc = new Document(sourcePath);
        doc.save(os, SaveFormat.PDF);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

1.2 接口代码

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.file.Paths;
import java.util.HashMap;

配置Application,记得配置,不然拦截器没有办法定位到文件

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.util.ResourceUtils;

import java.nio.file.Path;
import java.nio.file.Paths;
@SpringBootApplication
public class DemoApplication extends WebMvcConfigurationSupport {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
	Path path = Paths.get("");
	// private String projectPath = path.toAbsolutePath().toString();
	String staticPath = Paths.get("").toAbsolutePath().toString()+"/static";
	@Override
	protected void addResourceHandlers(ResourceHandlerRegistry registry) {
		//重定向设置
		registry.addResourceHandler("/html/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX + "/static/html/");
		registry.addResourceHandler("/css/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX + "/static/css/");
		registry.addResourceHandler("/js/**").addResourceLocations(ResourceUtils.CLASSPATH_URL_PREFIX + "/static/js/");
		registry.addResourceHandler("/download/**").addResourceLocations("file:"+staticPath +"/");
		registry.addResourceHandler("/preview/**").addResourceLocations("file:"+staticPath +"/");
		super.addResourceHandlers(registry);
	}
	@Bean
    public CharacterEncodingFilter characterEncodingFilter(){
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        filter.setForceEncoding(true);
        return filter;
    }
}

1.2.1 上传

获取前端返回的fileName参数后保存文件

@PostMapping("/upload")
public  String upload(@RequestParam("files") MultipartFile[] files) throws Exception{
    if(files != null && files.length>0){
        for(MultipartFile file:files){
            if(!file.isEmpty()){
                    System.out.println("staticPath:"+new File(staticPath).getCanonicalPath());
                    String fileName = file.getOriginalFilename();
                    File newFile = new File(staticPath+"/"+fileName);
                    String fileSuffix = Utils.getFileSuffix(fileName);
                    System.out.println("save:"+newFile.getCanonicalPath()+" suffix:"+fileSuffix);
                    if(!new File(newFile.getParent()).exists()){
                        System.out.println("make dirs:"+newFile.getParent());
                        new File(newFile.getParent()).mkdirs();
                    }
                    if (newFile.exists()){
                        newFile.delete();
                    }
                    try{
                        file.transferTo(new File(newFile.getCanonicalPath()));
                    }
                    catch(UnsupportedEncodingException e){
                        System.out.println("error:"+fileName);
                    }
                    if(fileSuffix.equals("docx")||fileSuffix.equals("doc")){
                        String targetPdfFilePath = String.join("/",pdfFilePath,fileName.replace("."+fileSuffix,".pdf"));
                        File targetPdfFile = new File(targetPdfFilePath);
                        if(!new File(pdfFilePath).exists()){
                            System.out.println("make dirs:"+pdfFilePath);
                            new File(pdfFilePath).mkdirs();
                        }
                        if(targetPdfFile.exists()){targetPdfFile.delete();}
                        System.out.println("transform to pdf:");
                        Utils.WordToPdf(newFile.getCanonicalPath(),targetPdfFilePath);
                        // PdfHelper3.doc2pdf(newFile.getCanonicalPath(),targetPdfFilePath);
                    }
                }
            }
    }
    return "redirect:/";

}

1.2.2 下载

public void download(@RequestParam("fileName") String fileName,HttpServletResponse response) throws IOException{
    String targetPath ;
    String fileNameEncode;
    try {
        fileNameEncode = URLEncoder.encode(fileName,"UTF-8");
        //替换+号否则空格解析会出错            
        fileNameEncode = fileNameEncode.replaceAll("\\+", "%20");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
        fileNameEncode = "";
    }
    targetPath = String.join("/",staticPath,fileName);
    File downloadFile = new File(targetPath);
    FileInputStream downFileInputStream = new FileInputStream(downloadFile);
    InputStream returnStream  = new BufferedInputStream(downFileInputStream);
    byte[] buffer = new byte[returnStream.available()];
    System.out.println("download:"+targetPath);
    System.out.println("file exist:"+new File(Paths.get(String.join("/", staticPath,fileName)).toAbsolutePath().toString()).exists());
    returnStream.read(buffer);
    returnStream.close();
    response.reset();
    //设置response的Header
    response.setCharacterEncoding("UTF-8");
    //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
    //attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
    //filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
    response.addHeader("Content-Disposition", "attachment;filename=" + fileNameEncode);
    //告知浏览器文件的大小
    response.addHeader("Content-Length", "" + downloadFile.length());
    OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
    response.setContentType("application/octet-stream");
    outputStream.write(buffer);
    outputStream.flush();
}

1.2.3 获取目录文件列表

@GetMapping("/getFileList")
public @ResponseBody String[] getFileList() throws IOException{
    HashMap<String,String> fileMap = new HashMap<String,String>();
    File staticFile = new File(staticPath);
    System.out.println("静态文件夹:"+staticFile.getCanonicalPath());
    if (staticFile!=null && staticFile.isDirectory())
    {
        File[] fileList = staticFile.listFiles();
        for(int i=0;i<fileList.length;i++){
            String fileName = fileList[i].getName();
            if(new File(String.join("/",staticPath,fileName)).isFile()){
                fileMap.put(fileName,String.valueOf(i));
            }
        }
        String[] fileNameList = new String[fileMap.keySet().size()]; 
        fileMap.keySet().toArray(fileNameList);
        return fileNameList ;
    }
    else{
        String[] fileNameList = new String[0]; 
        return fileNameList;
    }
} 

2 前端

<!DOCTYPE html>
<html>
<head>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上传下载预览demo界面</title>
</head>
<body>
    <form action="/upload" enctype="multipart/form-data" method="post">
        <input  name="files" type="file" multiple="multiple" value="请选择多个文件"/>
        <button type="submit">文件上传</button>
    </form>
    <div id="fileNameDiv">
    </div>
</body>
</html>
</body>
<script type="text/javascript" src="/js/jquery-3.1.1.min.js"></script>

<script type="text/javascript">

fileNameList = [];
function getFileList(){
  $.ajax({
    type:"get",
    url:"/getFileList",
    async:false,
    success:function(response){fileNameList = response;},
    error:function(){fileNameList = [];}
  })
}
getFileList();
for(i=0;i<fileNameList.length;i++){
  fileName = fileNameList[i];
  fileNameEncode = encodeURI(fileName)
  fileNameEncode = fileNameEncode.replace(/\+/g,'%2B');
  hrefPreview = `/preview?fileName=${fileNameEncode}`;
  hrefDelete = `/delete?fileName=${fileNameEncode}`;
  hrefDownload = `/download?fileName=${fileNameEncode}`;
  console.log(fileNameEncode);
  $("#fileNameDiv").append(
    `
    <a href=${hrefDelete}>删除</a> <a href=${hrefDownload}>下载</a>  <a href=${hrefPreview}>${fileName}</a> <br>
    `
  )
}
</script>

</html>