【发布时间】:2021-09-03 02:43:53
【问题描述】:
我已经编写了一个端点来返回一个包含多个 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!/:?]
【问题讨论】:
-
generateQrCode1不是线程安全的。对同一个 sysId 的多次请求将导致多个线程创建和删除同一个 zip 文件。 -
@AndyWilkinson 我已经添加了! getById() ,getProductById() 是对 db 的调用,它不会导致问题,因为我已经单独检查过。
-
抱歉,我编辑了我之前的评论,因为我意识到
generateQrCode1不是线程安全的。 -
@VeerabalaJ 是否使用相同的
sysId发出了多个请求?如果不是,是否有可能sysService.getById(sysId)与 2 个不同的sysId返回相同的productId?正如安迪所说,如果多个线程处理同名文件,它们可以删除彼此的文件。 -
另外,作为旁注,您在 Content-Disposition 标头中缺少文件名的结束双引号
标签: spring-boot kotlin file-io qr-code scalability