Skip to content

Commit 5ee7f41

Browse files
committed
Uploading files to Amazon S3 functional.
1 parent 57f77c7 commit 5ee7f41

File tree

12 files changed

+318
-5
lines changed

12 files changed

+318
-5
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ dependencies {
4040
compile "org.springframework.boot:spring-boot-starter-logging"
4141
compile "org.springframework.boot:spring-boot-autoconfigure"
4242
compile "org.grails:grails-core"
43+
compile group: "org.apache.jclouds", name: "jclouds-core", version: "1.9.2"
44+
compile group: "org.apache.jclouds", name: "jclouds-allblobstore", version: "1.9.2"
4345
console "org.grails:grails-console"
4446
profile "org.grails.profiles:plugin"
4547
provided "org.grails:grails-plugin-services"

grails-app/controllers/com/wizpanda/file/UploadController.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class UploadController {
3535
* }
3636
* </pre>
3737
*
38-
* Note: By default this endpoint is blocked to allow upload. You need to set following to your config in order
38+
* Note: By default this endpoint is blocked to allow upload. You need to set following to your service in order
3939
* to allow uploading files using this endpoint:
4040
*
4141
* <pre>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.wizpanda.file
2+
3+
class StoredFile {
4+
5+
String originalName
6+
String path
7+
8+
}

grails-app/services/com/wizpanda/file/FileUploadService.groovy

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package com.wizpanda.file
22

3+
import com.wizpanda.file.exception.FileUploadException
4+
import com.wizpanda.file.exception.InvalidFileGroupException
5+
import com.wizpanda.file.service.UploaderService
36
import org.springframework.context.MessageSource
4-
import org.springframework.web.multipart.commons.CommonsMultipartFile
7+
import org.springframework.web.multipart.MultipartFile
58

9+
import javax.annotation.PostConstruct
610
import java.nio.file.Files
711

812
class FileUploadService {
913

14+
private static Map<String, UploaderService> services = [:]
15+
1016
MessageSource messageSource
1117

1218
/**
@@ -18,7 +24,7 @@ class FileUploadService {
1824
* @throws FileNotFoundException
1925
* @throws IOException
2026
*/
21-
File saveTemporarily(CommonsMultipartFile multipartFile) throws FileNotFoundException, IOException {
27+
File saveTemporarily(MultipartFile multipartFile) throws FileNotFoundException, IllegalStateException, IOException {
2228
if (!multipartFile || multipartFile.isEmpty()) {
2329
log.debug "Received file is either empty or does not exists"
2430

@@ -29,19 +35,58 @@ class FileUploadService {
2935
String originalFilename = multipartFile.getOriginalFilename()
3036
File temporaryDirectory = Files.createTempDirectory(null).toFile()
3137

32-
log.debug "Uploaded file [$originalFilename] will be saved in [${temporaryDirectory.absolutePath}]"
38+
//log.debug "Uploaded file [$originalFilename] will be saved in [${temporaryDirectory.absolutePath}]"
3339

3440
File temporaryFile = new File(temporaryDirectory, originalFilename)
3541
multipartFile.transferTo(temporaryFile)
3642

3743
return temporaryFile
3844
} catch (IllegalStateException e) {
3945
log.error "Problem saving file", e
40-
throw new FileNotFoundException(e.message)
46+
throw e
4147

4248
} catch (IOException e) {
4349
log.error "Exception saving file", e
4450
throw e
4551
}
4652
}
53+
54+
File save(MultipartFile multipartFile, String groupName) throws FileUploadException {
55+
// TODO Add check for validating group name
56+
File temporaryFile
57+
try {
58+
temporaryFile = saveTemporarily(multipartFile)
59+
} catch (FileNotFoundException | IllegalStateException | IOException e) {
60+
throw new FileUploadException(e.message)
61+
}
62+
63+
return services.get(groupName).instance().saveFile(temporaryFile)
64+
}
65+
66+
@PostConstruct
67+
void verifyConfig() {
68+
//log.debug "Verifying all service"
69+
println "Verifying all service"
70+
71+
ConfigHelper.allGroups.each { Map.Entry groupConfigEntry ->
72+
String groupName = groupConfigEntry.key.toString()
73+
Map groupConfigValue = groupConfigEntry.value
74+
75+
if (!groupConfigValue.service) {
76+
throw new InvalidFileGroupException("The service API missing for [${groupName}]")
77+
}
78+
79+
Class<? extends UploaderService> serviceClass = groupConfigValue.service
80+
81+
UploaderService service
82+
try {
83+
service = serviceClass.newInstance(groupName, groupConfigValue)
84+
} catch (IllegalAccessException | InstantiationException | RuntimeException e) {
85+
log.error "Error creating uploader service object", e
86+
throw new InvalidFileGroupException("Error while creating the uploader service object");
87+
}
88+
89+
services[service.groupName] = service
90+
}
91+
}
4792
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.wizpanda.file
2+
3+
import com.wizpanda.file.exception.FileUploadException
4+
import grails.util.Holders
5+
6+
class ConfigHelper {
7+
8+
private static final String BASE_CONFIG = "fileUpload"
9+
10+
static Object getFlatConfig(String suffix) {
11+
return Holders.getFlatConfig()[BASE_CONFIG + "." + suffix]
12+
}
13+
14+
static ConfigObject getAllGroups() {
15+
return Holders.getConfig()[BASE_CONFIG + ".groups"]
16+
}
17+
18+
static ConfigObject getGroup(String group) {
19+
ConfigObject groupConfig = Holders.getConfig()[BASE_CONFIG + ".groups." + group]
20+
21+
if (!groupConfig || groupConfig.isEmpty()) {
22+
throw new FileUploadException("No service found under [${BASE_CONFIG}.${group}]")
23+
}
24+
25+
return groupConfig
26+
}
27+
28+
static void verifyGroup(String group) {
29+
ConfigObject groupConfig = Holders.getConfig()[BASE_CONFIG + ".groups." + group]
30+
31+
if (!groupConfig || groupConfig.isEmpty()) {
32+
throw new FileUploadException("No service found under [${BASE_CONFIG}.${group}]")
33+
}
34+
35+
// TODO complete implementation
36+
}
37+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.wizpanda.file.api
2+
3+
import com.wizpanda.file.service.AmazonS3UploaderService
4+
import grails.util.Environment
5+
import org.jclouds.ContextBuilder
6+
import org.jclouds.aws.s3.AWSS3Client
7+
import org.jclouds.blobstore.BlobStore
8+
import org.jclouds.blobstore.BlobStoreContext
9+
import org.jclouds.s3.domain.internal.MutableObjectMetadataImpl
10+
11+
import javax.activation.MimetypesFileTypeMap
12+
13+
abstract class AmazonS3Api implements StorageApi {
14+
15+
BlobStore blobStore
16+
AWSS3Client client
17+
BlobStoreContext context
18+
AmazonS3UploaderService service
19+
20+
void authenticate() {
21+
println service.accessKey
22+
println service.accessSecret
23+
context = ContextBuilder.newBuilder("aws-s3")
24+
.credentials(service.accessKey, service.accessSecret)
25+
.buildView(BlobStoreContext.class)
26+
println "Context created ${context.class}"
27+
28+
blobStore = context.getBlobStore()
29+
println "BlobStore ${blobStore.class}"
30+
31+
// Storing wrapped Api of S3Client with Apache JCloud
32+
client = context.unwrap().getApi()
33+
}
34+
35+
void close() {
36+
context.close()
37+
}
38+
39+
void setContentType(MutableObjectMetadataImpl mutableObjectMetadata, File file) {
40+
String contentType = new MimetypesFileTypeMap().getContentType(file.name)
41+
42+
if (contentType) {
43+
mutableObjectMetadata.getContentMetadata().setContentType(contentType)
44+
}
45+
}
46+
47+
@Override
48+
String getFileName(File file) {
49+
return UUID.randomUUID().toString()
50+
}
51+
52+
String getContainerName() {
53+
String name = service.container
54+
if (Environment.current != Environment.PRODUCTION) {
55+
name += "-" + Environment.current.name.toLowerCase()
56+
}
57+
58+
return name
59+
}
60+
61+
String getDirectory() {
62+
63+
}
64+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.wizpanda.file.api
2+
3+
import com.wizpanda.file.StoredFile
4+
import com.wizpanda.file.service.AmazonS3UploaderService
5+
import org.jclouds.aws.s3.blobstore.options.AWSS3PutObjectOptions
6+
import org.jclouds.blobstore.domain.Blob
7+
import org.jclouds.s3.domain.CannedAccessPolicy
8+
import org.jclouds.s3.domain.S3Object
9+
import org.jclouds.s3.domain.internal.MutableObjectMetadataImpl
10+
import org.jclouds.s3.domain.internal.S3ObjectImpl
11+
import org.springframework.web.multipart.MultipartFile
12+
13+
class AmazonS3PermanentURLApi extends AmazonS3Api {
14+
15+
AmazonS3PermanentURLApi(AmazonS3UploaderService service) {
16+
this.service = service
17+
}
18+
19+
@Override
20+
StoredFile saveFile(File file) {
21+
authenticate()
22+
upload(file)
23+
close()
24+
25+
return null
26+
}
27+
28+
String upload(File file) {
29+
AWSS3PutObjectOptions fileOptions = new AWSS3PutObjectOptions()
30+
fileOptions.withAcl(CannedAccessPolicy.PUBLIC_READ)
31+
32+
String fileName = getFileName(file)
33+
34+
MutableObjectMetadataImpl mutableObjectMetadata = new MutableObjectMetadataImpl()
35+
mutableObjectMetadata.setKey(fileName)
36+
setContentType(mutableObjectMetadata, file)
37+
38+
S3Object s3Object = new S3ObjectImpl(mutableObjectMetadata)
39+
s3Object.setPayload(file)
40+
41+
return client.putObject(getContainerName(), s3Object, fileOptions)
42+
/*Blob blob = blobStore.blobBuilder("blob-name")
43+
.payload(file)
44+
.contentLength(file.size())
45+
.build()
46+
47+
blobStore.putBlob(getContainerName(), blob)*/
48+
}
49+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.wizpanda.file.api
2+
3+
import com.wizpanda.file.StoredFile
4+
import com.wizpanda.file.service.UploaderService
5+
import org.springframework.web.multipart.MultipartFile
6+
7+
interface StorageApi {
8+
9+
UploaderService service
10+
11+
StoredFile saveFile(File file)
12+
13+
String getFileName(File file)
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.wizpanda.file.exception
2+
3+
class FileUploadException extends Exception {
4+
5+
FileUploadException(String message = null) {
6+
super(message)
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.wizpanda.file.exception
2+
3+
class InvalidFileGroupException extends Exception {
4+
5+
InvalidFileGroupException(String message = null) {
6+
super(message)
7+
}
8+
}

0 commit comments

Comments
 (0)