more codes
This commit is contained in:
@@ -101,6 +101,18 @@ class EmailStorage(
|
||||
return path.toString()
|
||||
}
|
||||
|
||||
fun storeUploadedPdf(propertyId: UUID, originalName: String?, bytes: ByteArray): String {
|
||||
val dir = Paths.get(rootPath, propertyId.toString(), "manual")
|
||||
Files.createDirectories(dir)
|
||||
val safeName = (originalName ?: UUID.randomUUID().toString()).replace(Regex("[^A-Za-z0-9._-]"), "_")
|
||||
val fileName = "${safeName}_${OffsetDateTime.now().toEpochSecond()}.pdf"
|
||||
val path = dir.resolve(fileName).normalize()
|
||||
val tmp = dir.resolve("${fileName}.tmp").normalize()
|
||||
Files.write(tmp, bytes)
|
||||
atomicMove(tmp, path)
|
||||
return path.toString()
|
||||
}
|
||||
|
||||
private fun atomicMove(tmp: Path, target: Path) {
|
||||
try {
|
||||
Files.move(tmp, target, java.nio.file.StandardCopyOption.ATOMIC_MOVE, java.nio.file.StandardCopyOption.REPLACE_EXISTING)
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.android.trisolarisserver.component
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.UUID
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
@Component
|
||||
class RoomImageStorage(
|
||||
@Value("\${storage.rooms.root:/home/androidlover5842/docs/rooms}")
|
||||
private val rootPath: String
|
||||
) {
|
||||
fun store(propertyId: UUID, roomId: UUID, file: MultipartFile): StoredRoomImage {
|
||||
val contentType = file.contentType ?: ""
|
||||
if (!contentType.startsWith("image/")) {
|
||||
throw IllegalArgumentException("Only image files are allowed")
|
||||
}
|
||||
val bytes = file.bytes
|
||||
val originalName = file.originalFilename ?: UUID.randomUUID().toString()
|
||||
val ext = extensionFor(contentType, originalName)
|
||||
|
||||
val dir = Paths.get(rootPath, propertyId.toString(), roomId.toString())
|
||||
Files.createDirectories(dir)
|
||||
val base = UUID.randomUUID().toString() + "_" + OffsetDateTime.now().toEpochSecond()
|
||||
val originalPath = dir.resolve("$base.$ext")
|
||||
val originalTmp = dir.resolve("$base.$ext.tmp")
|
||||
Files.write(originalTmp, bytes)
|
||||
atomicMove(originalTmp, originalPath)
|
||||
|
||||
val image = readImage(bytes)
|
||||
?: throw IllegalArgumentException("Unsupported image")
|
||||
val thumb = resize(image, 320)
|
||||
val thumbExt = if (ext.lowercase() == "jpg") "jpg" else "png"
|
||||
val thumbPath = dir.resolve("${base}_thumb.$thumbExt")
|
||||
val thumbTmp = dir.resolve("${base}_thumb.$thumbExt.tmp")
|
||||
ByteArrayInputStream(render(thumb, thumbExt)).use { input ->
|
||||
Files.copy(input, thumbTmp)
|
||||
}
|
||||
atomicMove(thumbTmp, thumbPath)
|
||||
|
||||
return StoredRoomImage(
|
||||
originalPath = originalPath.toString(),
|
||||
thumbnailPath = thumbPath.toString(),
|
||||
contentType = contentType,
|
||||
sizeBytes = bytes.size.toLong()
|
||||
)
|
||||
}
|
||||
|
||||
private fun readImage(bytes: ByteArray): BufferedImage? {
|
||||
return ByteArrayInputStream(bytes).use { input -> ImageIO.read(input) }
|
||||
}
|
||||
|
||||
private fun resize(input: BufferedImage, maxSize: Int): BufferedImage {
|
||||
val width = input.width
|
||||
val height = input.height
|
||||
if (width <= maxSize && height <= maxSize) return input
|
||||
val scale = if (width > height) maxSize.toDouble() / width else maxSize.toDouble() / height
|
||||
val newW = (width * scale).toInt()
|
||||
val newH = (height * scale).toInt()
|
||||
val output = BufferedImage(newW, newH, BufferedImage.TYPE_INT_RGB)
|
||||
val g = output.createGraphics()
|
||||
g.drawImage(input, 0, 0, newW, newH, null)
|
||||
g.dispose()
|
||||
return output
|
||||
}
|
||||
|
||||
private fun render(image: BufferedImage, format: String): ByteArray {
|
||||
val out = java.io.ByteArrayOutputStream()
|
||||
ImageIO.write(image, format, out)
|
||||
return out.toByteArray()
|
||||
}
|
||||
|
||||
private fun extensionFor(contentType: String, filename: String): String {
|
||||
return when {
|
||||
contentType.contains("png", true) -> "png"
|
||||
contentType.contains("jpeg", true) || contentType.contains("jpg", true) -> "jpg"
|
||||
filename.contains(".") -> filename.substringAfterLast('.').lowercase()
|
||||
else -> "png"
|
||||
}
|
||||
}
|
||||
|
||||
private fun atomicMove(tmp: Path, target: Path) {
|
||||
try {
|
||||
Files.move(tmp, target, java.nio.file.StandardCopyOption.ATOMIC_MOVE, java.nio.file.StandardCopyOption.REPLACE_EXISTING)
|
||||
} catch (_: Exception) {
|
||||
Files.move(tmp, target, java.nio.file.StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class StoredRoomImage(
|
||||
val originalPath: String,
|
||||
val thumbnailPath: String,
|
||||
val contentType: String,
|
||||
val sizeBytes: Long
|
||||
)
|
||||
Reference in New Issue
Block a user