为什么以下文件生成方法 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)))
附带说明一下,您可能应该使用临时文件夹而不是简单的文件名,因为目前这些文件是在当前工作目录中创建的。
我已经写了一个端点到 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)))
附带说明一下,您可能应该使用临时文件夹而不是简单的文件名,因为目前这些文件是在当前工作目录中创建的。