使用Axios将Vue前端的pdf文件上传/下载到Spring Backend

时间:2018-11-30 14:39:09

标签: javascript spring spring-boot vue.js axios

在后端使用spring-boot 2.0.4,在前端使用vue 2.5.16 / axios 0.18.0,我希望将PDF文件上传到后端db并从我的数据库中检索它们前端。

最初,我从这个示例的春天部分得到启发:https://grokonez.com/frontend/angular/angular-6/angular-6-client-upload-files-download-files-to-mysql-with-springboot-restapis

Axios部分的要点是: https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743

我的代码如下:

  • 使用axios在Vue中上传文件(将this.file变量使用“ input type =“ file”“形式输入正确设置为我的文件,AxiosService()仅用于设置正确的baseUrl并包括具有令牌的授权标头):

    createRecord() {
      let formData = new FormData();
      formData.append("file", this.file);
      AxiosService()
        .post("/commands/material/", formData, {
           headers: {
             "Content-Type": "multipart/form-data"
           }
      })
      .then(response => {
        console.log("File uploaded");
      })
      .catch(error => {
        console.log(error);
      });
    
  • 处理上载的弹簧部分如下所示。在我的实体中,content字段定义为带@Lob注释的byte []。

    @BasePathAwareController
    @RequestMapping("/commands/material")
    public class MaterialCommandHandler {
        @Autowired
        MaterialRepository MaterialRepository;
    
        @RequestMapping(method=POST, path = "/")
        public ResponseEntity create(@RequestParam("file") MultipartFile file){
            MaterialEntity material = new MaterialEntity();
            material.setName(file.getOriginalFilename());
            material.setMimetype(file.getContentType());
    
            try {
                material.setContent(file.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            try {
                MaterialRepository.save(material);
            } catch (Exception e) {
                if (e instanceof DataIntegrityViolationException) {
                    throw new InvalidCommandException("Data is invalid for creation.");
                }
                throw(e);
            }
            return ResponseEntity.status(HttpStatus.CREATED).body(material.getId());
    }
    

使用此代码,该条目已在数据库中正确创建,mysql中的content字段为longblob类型。

  • 定义为返回文件内容的方法:

    @RequestMapping(method = GET, path = "/download/{fileId}")
    public ResponseEntity<byte[]> getFile(@PathVariable Long fileId) {
        Optional<MaterialEntity> fileOptional = materialRepository.findById(fileId);
    
        if(fileOptional.isPresent()){
            FrancaisMaterialEntity file = fileOptional.get();
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachement; filename=\"" + file.getName() + "\"");
            return ResponseEntity.ok()
                .headers(headers)
                .body(file.getContent());
        }
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
    }
    
  • 最后,使用axios从前端发送的GET方法:

    downloadFile() {
        AxiosService()
          .get(`/commands/material/download/${this.material.id}`, {
            responseType: "blob",
          })
          .then(response => {
            console.log(response);
            const url = window.URL.createObjectURL(new Blob([response.data]));
            const link = document.createElement("a");
            link.href = url;
            link.setAttribute("download", "CalculRanking.pdf");
            document.body.appendChild(link);
            link.click();
            link.parentNode.removeChild(link);
          })
          .catch(error => {
            console.log(error);
            this.errorMessage = error.response.data.message;
          });
      }
    

当尝试下载文件时,导航器中的弹出窗口会正确显示,但不幸的是,由于Chrome指出:“错误无法加载PDF文档”,因此下载的.pdf文件似乎已损坏。我无法打开它在预览中都不会。

我认为问题出在过程中的某个时刻,内容解释有误。我做了很多研究,但没有尝试过的解决方案使它起作用(我尝试过的一些事情:在值中添加值为“ application / pdf”的“ Accept”标头,并在get请求中设置“ responseType:arrayBuffer”)我决定在这里问这个问题。预先感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

您可以通过以下方式更改方法getFile

@GetMapping("/download/{fileId}")
@CrossOrigin
@ResponseBody
public ResponseEntity<InputStreamResource> getFile(@PathVariable(required = true, value = "fileId") Long fileId,
        HttpServletRequest request) throws IOException {

    Optional<MaterialEntity> fileOptional = materialRepository.findById(fileId);
    if (ensemblesRegles.isPresent()) {
        String fileName = "example.xlsx";
        MediaType mediaType = MediaType.parseMediaType("application/vnd.ms-excel");
        File file = new File(fileName); //the fileUtils is org.apache.commons.io.FileUtils;
        FileUtils.writeByteArrayToFile(file, fileOptional.get()); // Hope that your get return a byte[] array 
        InputStreamResource resource = new InputStreamResource(new FileInputStream(file));

        return ResponseEntity.ok()
                // Content-Disposition
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
                // Content-Type
                .contentType(mediaType)
                // Contet-Length
                .contentLength(file.length()) //
                .body(resource);
    }
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}

答案 1 :(得分:1)

您为什么不看Spring Content。旨在完全按照您的意图进行操作,并将一个或多个内容对象与Spring数据实体相关联。

要将其添加到现有的Spring Boot项目中,请执行以下操作:-

  

pom.xml

   <!-- Java API -->
   <dependency>
      <groupId>com.github.paulcwarren</groupId>
      <artifactId>spring-content-jpa-boot-starter</artifactId>
      <version>0.4.0</version>
   </dependency>

   <!-- REST API -->
   <dependency>
      <groupId>com.github.paulcwarren</groupId>
      <artifactId>spring-content-rest-boot-starter</artifactId>
      <version>0.4.0</version>
   </dependency>
  

MaterialEntity.java

@Entity
public class MaterialEntity {
   @Id
   @GeneratedValue
   private long id;

   ...other existing fields...

   @ContentId
   private String contentId;

   @ContentLength
   private long contentLength = 0L;

   @MimeType
   private String mimeType = "text/plain";

   ...
}
  

MaterialEntityContentStore.java

@StoreRestResource(path="materialEntityContents")
public interface MaterialEntityContentStore extends ContentStore<MaterialEntity, String> {
}

这是获取REST端点所需要做的全部工作,这些端点将使您可以存储和检索与每个MaterialEntity关联的内容。实际工作方式与Spring Data非常相似。当您的应用程序启动时,Spring Content将看到spring-content-jpa-boot-starter依赖项,并且知道您要将内容存储在数据库中。然后,它将在数据库中创建一个架构来这样做,并注入MaterialEntityContentStore接口的JPA实现。它还将看到spring-content-rest-boot-starter并将注入与内容存储接口对话的REST端点。意味着您不必自己执行任何此操作。

例如,

curl -X POST /materialEntityContents/{materialEntityId} -F "file=@/path/to/image.jpg"

会将图像存储在数据库中,并将其与ID为materialEntityId的材料实体相关联。

curl /materialEntityContents/{materialEntity}

将再次获取它,依此类推...实际上也完全支持完整的CRUD和视频流。

具体来说,有一个(非SpringBoot)MySQL示例here

您还可以通过将spring-content-jpa-boot-starter依赖项交换为适当的Spring Content Storage模块,来决定将内容存储在文件系统上或S3中的其他位置。每种存储类型的示例均为here

不幸的是,没有前端vuejs示例,但是我们确实有angularjs 1.x示例here。这可能对前端有所帮助,因为它们是类似的技术(以我对两者的有限经验!)。