为什么以下文件生成方法 return FileNotFoundException 会同时请求此端点?

Why does the below file generation method return FileNotFoundException on simultaneous request to this endpoint?

我已经写了一个端点到 return 一个包含多个 qr 的 zip,其值基于我的数据库中的详细信息。当我通过向该端点发出多个请求来进行负载测试时,它会抛出 FileNotFoundException。但如果在特定时间间隔发出请求,则不会发生这种情况。

@GetMapping(value = ["/{sysId}/code"], produces = ["application/zip"])
fun generateQrCode1(@PathVariable sysId: Int): ResponseEntity<InputStreamResource> {
    val sysDetails = sysService.getById(sysId)
    val productDetails = productService.getProductByProductId(sysDetails.productId)
    val zipName = sysDetails.productId.toString() + ".zip"
    FileOutputStream(zipName).use { fileOutputStream ->
        ZipOutputStream(fileOutputStream).use { zipOutputStream ->
            for (i in 1..sysDetails.length) {
                val u = "http://" + "$domain/" + "?sysId=${sysId}&pid=$i"
                val x = generateQRCodeWithText(u, 350, 300, arrayOf("QR CODE TEST"))
                val byteArrayOutputStream = ByteArrayOutputStream()
                byteArrayOutputStream.write(x!!)
                val zipEntry = ZipEntry(
                    "${
                        productDetails.name
                    }-${sysId}-$i.jpg"
                )
                zipOutputStream.putNextEntry(zipEntry)
                byteArrayOutputStream.writeTo(zipOutputStream)
            }
        }
    }
    try {
        return ResponseEntity
            .ok()
            .header("Content-Disposition", "attachment; filename=\"$zipName")
            .body(InputStreamResource(FileInputStream(zipName)))
    } finally {
        java.nio.file.Files.deleteIfExists(Paths.get(zipName))
    }
  
    fun generateQRCodeWithText(data: String?, width: Int?, height: Int, text: Array<String?>): ByteArray? {
    return try {
        val qrCodeWriter = QRCodeWriter()
        val bitMatrix = qrCodeWriter.encode(data, BarcodeFormat.QR_CODE, width!!, height)
        val pngOutputStream = ByteArrayOutputStream()
        MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream)
        var pngData = pngOutputStream.toByteArray()

        if (text.size > 0) {
            val totalTextLineToadd = text.size
            val `in`: InputStream = ByteArrayInputStream(pngData)
            val image: BufferedImage = ImageIO.read(`in`)
            val outputImage =
                BufferedImage(image.width, image.height + 25 * totalTextLineToadd, BufferedImage.TYPE_INT_ARGB)
            val g: Graphics = outputImage.graphics
            val g2d = g as Graphics2D
            g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g.setColor(Color.WHITE)
            g.fillRect(0, 0, outputImage.width, outputImage.height)
            g.drawImage(image, 0, 0, image.width, image.height, null)
            g.setFont(Font("Arial Black", Font.BOLD, 20))
            val textColor: Color = Color.BLUE
            g.setColor(textColor)
            val fm: FontMetrics = g.getFontMetrics()
            var startingYposition = height + 5
            for (displayText in text) {
                g.drawString(
                    displayText,
                    outputImage.width / 2 - fm.stringWidth(displayText) / 2,
                    startingYposition
                )
                startingYposition += 20
            }
            val baos = ByteArrayOutputStream()
            ImageIO.write(outputImage, "PNG", baos)
            baos.flush()
            pngData = baos.toByteArray()
            baos.close()
        }
        pngData
    } catch (ex: WriterException) {
        throw ex
    } catch (ex: IOException) {
        throw ex
    }
}

同时多个请求出错:

java.io.FileNotFoundException: 2889.zip (No such file or directory)
at java.io.FileInputStream.open0(Native Method) ~[?:1.8.0_292]
 at java.io.FileInputStream.open(FileInputStream.java:195) ~[?:1.8.0_292]
at java.io.FileInputStream.<init>(FileInputStream.java:138) ~[?:1.8.0_292]
 at java.io.FileInputStream.<init>(FileInputStream.java:93) ~[?:1.8.0_292]
 at com.mangoChain.qrApi.QrApi.generateQrCode1(QrApi.kt:136) ~[classes!/:?]

您应该确保您的本地文件名 (zipName) 是唯一的,否则多个线程可能会删除彼此的文件。

您在评论中提到 productId 可能会发生冲突,因此您可以根据 sysId 来命名您的文件,而不是(或除此之外)productId。例如:

val zipName = "$sysId-${sysDetails.productId}.zip"

请注意,您只需要使您的本地文件名唯一 (zipName),但您仍然可以在 Content-Disposition header 中保留您想要的 user-visible 文件名]:

return ResponseEntity
    .ok()
    .header("Content-Disposition", "attachment; filename=\"${sysDetails.productId}.zip\"")
    .body(InputStreamResource(FileInputStream(zipName)))

附带说明一下,您可能应该使用临时文件夹而不是简单的文件名,因为目前这些文件是在当前工作目录中创建的。