Здравствуйте. у нас есть s3 хранилище для файлов. В этом хранилище есть папка public, файлы в ней доступны всем без авторизации. По дефолту мы хотим чтоб все файлы грузились не в эту папку. Но в определенных случаях нужно грузить в публичную папку. Можете подсказать можно что сделать в аддоне? Или нужно будет свою логику полностью писать для конкретных файловых полей?
В общем удалось так.
Отнаследовался от AwsFileStorage
переопределил createFileKey
чтоб тот добавлял в начале название папки public
и переопределил saveStream
чтоб в запросах s3Client
добавить .acl(ObjectCannedACL.PUBLIC_READ)
@Component("PublicAwsFileStorage")
class PublicAwsFileStorage: AwsFileStorage(DEFAULT_STORAGE_NAME) {
private val log: Logger = LoggerFactory.getLogger(PublicAwsFileStorage::class.java)
/**
* copied from [AwsFileStorage#saveStream] with addition of PUBLIC_READ ACL
*/
override fun saveStream(fileName: String, inputStream: InputStream, parameters: Map<String, Any>): FileRef {
val fileKey = createFileKey(fileName)
val bucket = this.bucket
val s3ChunkSizeBytes = this.chunkSize * 1024
val fileRefParameters = Maps.toMap(parameters.keys) { key -> parameters[key].toString() }
val fileRef = FileRef(getStorageName(), fileKey, fileName, fileRefParameters)
try {
BufferedInputStream(inputStream, s3ChunkSizeBytes).use { bos ->
val chunkBytes = ByteArray(s3ChunkSizeBytes)
var nBytes = bos.read(chunkBytes)
val s3Client = s3ClientReference.get()
if (nBytes < s3ChunkSizeBytes) {
s3Client.putObject({ objectBuilder: PutObjectRequest.Builder ->
objectBuilder
.bucket(bucket)
.key(fileKey)
.acl(ObjectCannedACL.PUBLIC_READ)
.build()
}, fromBytes(chunkBytes, nBytes))
return fileRef
}
val response = s3Client.createMultipartUpload { uploadBuilder ->
uploadBuilder
.bucket(bucket)
.key(fileKey)
.acl(ObjectCannedACL.PUBLIC_READ)
}
val completedParts = mutableListOf<CompletedPart>()
val partBuilder = UploadPartRequest.builder()
.bucket(bucket)
.key(fileKey)
.uploadId(response.uploadId())
var partNumber = 1
while (0 < nBytes) {
val partResponse = s3Client.uploadPart(
partBuilder
.partNumber(partNumber)
.build(), fromBytes(chunkBytes, nBytes)
)
val completedPart = CompletedPart.builder()
.partNumber(partNumber)
.eTag(partResponse.eTag())
.build()
completedParts.add(completedPart)
nBytes = bos.read(chunkBytes)
partNumber++
}
s3Client.completeMultipartUpload { completeBuilder ->
completeBuilder
.bucket(bucket)
.key(fileKey)
.uploadId(response.uploadId())
.multipartUpload { multipartBuilder ->
multipartBuilder.parts(completedParts)
}
}
return fileRef
}
} catch (e: IOException) {
log.error("Error saving file to S3 storage", e)
val message = String.format("Could not save file %s.", fileName)
throw FileStorageException(FileStorageException.Type.IO_EXCEPTION, message)
} catch (e: SdkException) {
log.error("Error saving file to S3 storage", e)
val message = String.format("Could not save file %s.", fileName)
throw FileStorageException(FileStorageException.Type.IO_EXCEPTION, message)
}
}
override fun createFileKey(fileName: String): String {
return "public/" + super.createFileKey(fileName)
}
companion object {
const val DEFAULT_STORAGE_NAME = "publicS3"
}
}
В Редакторах у поля fileStorageUploadField
указываю fileStorageName="publicS3"
В пропертях указал jmix.core.default-file-storage=s3
Вроде бы все работает.
2 симпатии