• 请不要在回答技术问题时复制粘贴 AI 生成的内容
sandman511
V2EX  ›  程序员

SpringBoot 接口怎么传数据库中的 BLOB 图片给前端

  •  1
     
  •   sandman511 · Sep 25, 2020 · 5132 views
    This topic created in 2231 days ago, the information mentioned may be changed or developed.

    单张或者多张的 求大佬讲个方案 现在是能传 但想要个最佳方案。。虽然数据库存图片就离谱。。。。

    26 replies  •  2020-09-26 21:00:11 +08:00
    RedBeanIce
    RedBeanIce
    Sep 25, 2020
    1
    都 springboot 了,数据库还存图片的二进制吗?
    myCupOfTea
    myCupOfTea
    Sep 25, 2020
    2
    数据库存图片确实离谱
    muskill
    muskill
    Sep 25, 2020
    3
    存 base64 呢
    lower
    lower
    Sep 25, 2020
    4
    现在的方案是什么样的呢?
    orcusfox
    orcusfox
    Sep 25, 2020 via iPhone❤️1
    5
    blob 直接当成文件下载送出去,前端直接用 img src 拿
    monkeyWie
    monkeyWie
    Sep 25, 2020
    6
    提供一个接口,然后以字节流的方式写出图片不就行了吗,注意设置好正确的 Content-Type 就行
    yazinnnn
    yazinnnn
    Sep 25, 2020
    7
    说实话,数据库存图片挺扯淡的
    fhsan
    fhsan
    Sep 25, 2020
    8
    http body stream
    charten
    charten
    Sep 25, 2020❤️1
    9
    我从前端的角度谈一下,我们这边请求 blob 实际上就是请求二进制流,跟普通的请求没什么区别。你只要把图片的二进制数据放进 body 就可以了。另外,如果有请求多张的情况,可以跟前端约定好一个二进制格式,比如以某个字符,比如 0x3b 之类的作为文件的开头,后面加 4 个字节用于表示图片的二进制长度:
    0x3b 0x00 0x00 0x11 0xff <图片数据....4607 个字节>
    0x3b 0x00 0x00 0x22 0xff <图片数据...8959 个字节>
    ...
    securityCoding
    securityCoding
    Sep 25, 2020
    10
    不要在错误的路上越走越远 , 掉头吧
    wysnylc
    wysnylc
    Sep 25, 2020
    11
    调头,OSS
    justlikemaki
    justlikemaki
    Sep 25, 2020
    12
    敏感图片你们也存 oss ?
    crclz
    crclz
    Sep 25, 2020❤️1
    13
    @GetMapping("{id}")
    public ResponseEntity<byte[]> getFile(@PathVariable long id) {
    var blob = blobRepository.findById(id).orElse(null);
    return ResponseEntity.ok()
    .contentType(MediaType.IMAGE_JPEG)
    .body(blob.getData());
    }
    spacebound
    spacebound
    Sep 25, 2020
    14
    @monkeyWie +1,前端直接不用处理,调接口就行了
    0x666666
    0x666666
    Sep 25, 2020
    15
    都 0202 年了,微服务都用上了,数据库还存图片?
    ByteRan
    ByteRan
    Sep 25, 2020
    16
    base64
    NotFounder
    NotFounder
    Sep 25, 2020
    17
    专业的事交给专业的服务
    zliea
    zliea
    Sep 25, 2020
    18
    InputStreamResource resource = new InputStreamResource(gridFSDBFile.getInputStream(),
    gridFSDBFile.getFilename());

    return (ResponseEntity<Resource>) ResponseEntity
    .ok()
    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + gridFSDBFile.getFilename() + "\"")
    .contentLength(gridFSDBFile.getLength())
    .contentType(MediaType.APPLICATION_OCTET_STREAM)
    .body(resource);
    zliea
    zliea
    Sep 25, 2020❤️1
    19
    如果是 byte[], 可以使用 ByteArrayResource
    zjsxwc
    zjsxwc
    Sep 25, 2020
    20
    还不如图片加密后文件保存本地,mysql 存图片解密密码,需要的时候通过密码和加密后的文件得到原图返回给客户端
    hejingyuan199
    hejingyuan199
    Sep 25, 2020
    21
    虽然我不知道如何回答。
    但我想附和一下,我也是把图片存数据库的。

    我现在的项目是个测试性项目,不是正规使用。
    我们的数据库供所有开发人员同时使用的。

    不同的开发人员有不同的功能,功能互不干涉,但是共用一个数据库和表。
    如果图片存在服务器本地,数据库只存图片名称,那大家各自在测试时都拿不到图片。
    所以我把图片转成 Base64 字符串存进数据库了。
    还有的表里存着 Blob 类型的用户上传的附件(文件或图片)。

    一直看到大家说大文件不应该存数据库,
    也许未来转为生产环境时候再改吧。

    如果数据库存着 Base64 字符串,往前端发送应该比较简单了吧。
    Blob 我看到我们的这边用的是,
    (代码风格很渣,随便接受 Critics )

    /***** Student Chose One Application*****/
    @RequestMapping(value="/history/{ticketNum}", method=RequestMethod.GET)
    public String historyApplication(@PathVariable String ticketNum, Model model, HttpServletRequest request) {
    String search = (String) request.getSession().getAttribute("searchTicketNum");
    Optional<Application> app = applicationService.findApplication(ticketNum);
    if (app.isPresent()) {
    model.addAttribute("applicationObj", app.get());
    Optional<User> user = AIBTuserdetailsService.findByUserName(app.get().getCreatedBy());
    model.addAttribute("userObj",user.get());
    model.addAttribute("urlTicketNum", ticketNum);

    model.addAttribute("documents", applicationService.getDocumentUrl(ticketNum, app.get().getId()));
    --------------------------------------------------------------------------------------------------
    //调用 blob 类型的文件

    model.addAttribute("logs", applicationService.getAuditLog(app.get().getId()));
    //Optional<Course> course = courseService.findByCourseid(user.get().getCurrentCourseCode());
    //if(course.isPresent()) {
    // model.addAttribute("course", course.get().getCourseName());}
    //else {
    // model.addAttribute("course", "Not found");
    //}
    }
    model.addAttribute("applicationList", applicationService.findApplications(search));
    return "student/history";
    }

    In ApplicationService.java

    public Map<String, String> getDocumentUrl(String ticketNum, int applicationId) {
    Map<String, String> result = new HashMap<>();
    for (String fileName : documentRepository.findAllFileNameByApplicationId(applicationId)) {
    ---------------------------------------------------------------------------------
    //get document out from Database

    if (!fileName.isEmpty()) {
    try {
    result.put(fileName, ticketNum + "/" + URLEncoder.encode(fileName, String.valueOf(StandardCharsets.UTF_8)));
    } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    }
    }
    }
    if (result.isEmpty()) {
    return null;
    } else {
    return result;
    }
    }

    In DocumentRepository.java

    public interface DocumentRepository extends JpaRepository<Document, Integer> {
    Optional<Document> findByFileNameAndApplicationId(String fileName, int applicationId);
    @Query(value="select file_name from document where application_id =:application_id", nativeQuery=true)
    List<String> findAllFileNameByApplicationId(@Param("application_id") int applicationId);
    }


    //This is parts of Class Document

    @Entity
    @Table(name = "Document")
    public class Document {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private int applicationId;
    private String fileName;
    private String fileType;
    @Lob
    private byte[] data;
    ...
    }

    仅供参考
    这个流程似乎就是前面一些 V 友说的以文件下载方式传到前端的。

    前端是这样的:
    <div class="three">
    <p>
    <b>Attachment:</b>
    </p>
    <div class="links">
    <a target="_blank" th:each="element : ${documents}"
    th:href="@{/student/downloadFile/{url}(url=${element.value})}"><img th:src="@{/image/qubiezhen.png}">[[${element.key}]]</a>
    </div>
    </div>
    hejingyuan199
    hejingyuan199
    Sep 25, 2020
    22
    @hejingyuan199

    补上最关键的数据传输

    @RequestMapping(value="/downloadFile/{ticketNum}/{fileName}", method=RequestMethod.GET)
    public ResponseEntity<Resource> downloadFile(@PathVariable String ticketNum, @PathVariable String fileName) {
    // Load file from database
    Optional<Document> doc = applicationService.getDocument(ticketNum, fileName, Boolean.FALSE);

    if (doc.isPresent()) {
    return ResponseEntity.ok()
    .contentType(MediaType.parseMediaType(doc.get().getFileType()))
    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + doc.get().getFileName() + "\"")
    .body(new ByteArrayResource(doc.get().getData()));
    } else {
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
    }
    bxd5812127
    bxd5812127
    Sep 25, 2020
    23
    后端给前端提供两个接口,一个拿所有图片的 id,一个根据 id 拿一个图片,前端根据 id 排队拿图片有没有搞头,拿完一个在拿一个。
    THESDZ
    THESDZ
    Sep 25, 2020
    24
    后端应该提供的能力是 <img src="url"/> 的这个 url,内部实现可以直接通过 resp 传输流
    hyperbin
    hyperbin
    Sep 26, 2020 via Android
    25
    @charten 怎么保证图片里没有 0x3b ?
    charten
    charten
    Sep 26, 2020
    26
    @hyperbin 不用保证呀,又不是拿 0x3b 去当分割符,0x3b 只是一个标识符,标识符一个块的开始,这个标识符可以 0x00 到 0xff 的任意一个字符,关键是紧跟着的后四位字节,代表了这个块的长度,你真正要拿图片要通过这个长度去拿,就是 slice 方法。当然也可以根据这个长度跳过去拿下一张图片。psd 文件的二进制格式基本上都是这样的
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   4968 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 77ms · UTC 01:38 · PVG 09:38 · LAX 17:38 · JFK 20:38
    ♥ Do have faith in what you're doing.