SpringBoot 接口怎么传数据库中的 BLOB 图片给前端 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
sandman511
V2EX    程序员

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

  •  1
     
  •   sandman511 2020-09-25 09:18:08 +08:00 4434 次点击
    这是一个创建于 1910 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    26 条回复    2020-09-26 21:00:11 +08:00
    RedBeanIce
        1
    RedBeanIce  
       2020-09-25 09:20:42 +08:00
    都 springboot 了,数据库还存图片的二进制吗?
    myCupOfTea
        2
    myCupOfTea  
       2020-09-25 09:22:56 +08:00
    数据库存图片确实离谱
    muskill
        3
    muskill  
       2020-09-25 09:22:59 +08:00
    存 base64 呢
    lower
        4
    lower  
       2020-09-25 09:27:28 +08:00
    现在的方案是什么样的呢?
    orcusfox
        5
    orcusfox  
       2020-09-25 09:27:49 +08:00 via iPhone   1
    blob 直接当成文件下载送出去,前端直接用 img src 拿
    monkeyWie
        6
    monkeyWie  
       2020-09-25 09:28:00 +08:00
    提供一个接口,然后以字节流的方式写出图片不就行了吗,注意设置好正确的 Content-Type 就行
    yazinnnn
        7
    yazinnnn  
       2020-09-25 09:44:40 +08:00
    说实话,数据库存图片挺扯淡的
    fhsan
        8
    fhsan  
       2020-09-25 09:49:35 +08:00
    http body stream
    charten
        9
    charten  
       2020-09-25 09:56:19 +08:00   1
    我从前端的角度谈一下,我们这边请求 blob 实际上就是请求二进制流,跟普通的请求没什么区别。你只要把图片的二进制数据放进 body 就可以了。另外,如果有请求多张的情况,可以跟前端约定好一个二进制格式,比如以某个字符,比如 0x3b 之类的作为文件的开头,后面加 4 个字节用于表示图片的二进制长度:
    0x3b 0x00 0x00 0x11 0xff <图片数据....4607 个字节>
    0x3b 0x00 0x00 0x22 0xff <图片数据...8959 个字节>
    ...
    securityCoding
        10
    securityCoding  
       2020-09-25 10:02:57 +08:00
    不要在错误的路上越走越远 , 掉头吧
    wysnylc
        11
    wysnylc  
       2020-09-25 10:16:40 +08:00
    调头,OSS
    justlikemaki
       
    justlikemaki  
       2020-09-25 10:34:31 +08:00
    敏感图片你们也存 oss ?
    crclz
        13
    crclz  
       2020-09-25 10:54:05 +08:00   1
    @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
        14
    spacebound  
       2020-09-25 11:19:15 +08:00
    @monkeyWie +1,前端直接不用处理,调接口就行了
    0x666666
        15
    0x666666  
       2020-09-25 11:28:03 +08:00
    都 0202 年了,微服务都用上了,数据库还存图片?
    tanranran
        16
    tanranran  
       2020-09-25 11:28:44 +08:00
    base64
    radiocontroller
        17
    radiocontroller  
       2020-09-25 11:30:32 +08:00
    专业的事交给专业的服务
    zliea
        18
    zliea  
       2020-09-25 11:32:45 +08:00
    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
        19
    zliea  
       2020-09-25 11:33:37 +08:00   1
    如果是 byte[], 可以使用 ByteArrayResource
    zjsxwc
        20
    zjsxwc  
       2020-09-25 11:46:19 +08:00
    还不如图片加密后文件保存本地,mysql 存图片解密密码,需要的时候通过密码和加密后的文件得到原图返回给客户端
    hejingyuan199
        21
    hejingyuan199  
       2020-09-25 15:27:58 +08:00
    虽然我不知道如何回答。
    但我想附和一下,我也是把图片存数据库的。

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

    不同的开发人员有不同的功能,功能互不干涉,但是共用一个数据库和表。
    如果图片存在服务器本地,数据库只存图片名称,那大家各自在测试时都拿不到图片。
    所以我把图片转成 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
        22
    hejingyuan199  
       2020-09-25 15:33:30 +08:00
    @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
        23
    bxd5812127  
       2020-09-25 16:15:34 +08:00
    后端给前端提供两个接口,一个拿所有图片的 id,一个根据 id 拿一个图片,前端根据 id 排队拿图片有没有搞头,拿完一个在拿一个。
    THESDZ
        24
    THESDZ  
       2020-09-25 16:34:35 +08:00
    后端应该提供的能力是 <img src="url"/> 的这个 url,内部实现可以直接通过 resp 传输流
    hyperbin
        25
    hyperbin  
       2020-09-26 11:56:53 +08:00 via Android
    @charten 怎么保证图片里没有 0x3b ?
    charten
        26
    charten  
       2020-09-26 21:00:11 +08:00
    @hyperbin 不用保证呀,又不是拿 0x3b 去当分割符,0x3b 只是一个标识符,标识符一个块的开始,这个标识符可以 0x00 到 0xff 的任意一个字符,关键是紧跟着的后四位字节,代表了这个块的长度,你真正要拿图片要通过这个长度去拿,就是 slice 方法。当然也可以根据这个长度跳过去拿下一张图片。psd 文件的二进制格式基本上都是这样的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     978 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 22:45 PVG 06:45 LAX 14:45 JFK 17:45
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86