-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
eae1bc0
commit e9ef06a
Showing
5 changed files
with
405 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
#Build Number for ANT. Do not edit! | ||
#Thu Oct 26 10:22:47 CEST 2023 | ||
build.number=3 | ||
#Tue Oct 31 17:27:17 CET 2023 | ||
build.number=4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
198 changes: 198 additions & 0 deletions
198
source/java/src/org/lucee/extension/resource/s3/function/S3Download.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package org.lucee.extension.resource.s3.function; | ||
|
||
import java.io.BufferedInputStream; | ||
import java.io.BufferedReader; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.nio.charset.Charset; | ||
import java.util.Arrays; | ||
|
||
import org.lucee.extension.resource.s3.S3; | ||
import org.lucee.extension.resource.s3.util.print; | ||
|
||
import com.amazonaws.services.s3.model.S3Object; | ||
|
||
import lucee.commons.io.res.Resource; | ||
import lucee.loader.engine.CFMLEngine; | ||
import lucee.loader.engine.CFMLEngineFactory; | ||
import lucee.runtime.PageContext; | ||
import lucee.runtime.exp.PageException; | ||
import lucee.runtime.type.FunctionArgument; | ||
import lucee.runtime.type.UDF; | ||
import lucee.runtime.util.Cast; | ||
|
||
public class S3Download extends S3Function { | ||
|
||
private static final long serialVersionUID = 8926919958105910628L; | ||
|
||
public static final short TYPE_ANY = 0; | ||
public static final short TYPE_BOOLEAN = 2; | ||
public static final short TYPE_STRING = 7; | ||
|
||
public static final short MODE_LINE = 1; | ||
public static final short MODE_BINARY = 2; | ||
public static final short MODE_STRING = 4; | ||
|
||
@Override | ||
public Object invoke(PageContext pc, Object[] args) throws PageException { | ||
CFMLEngine eng = CFMLEngineFactory.getInstance(); | ||
Cast cast = eng.getCastUtil(); | ||
if (args.length > 8 || args.length < 2) throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 2, 8, args.length); | ||
|
||
// required | ||
String bucketName = cast.toString(args[0]); | ||
String objectName = cast.toString(args[1]); | ||
|
||
// optional | ||
Object target = args.length > 2 && args[2] != null ? args[2] : null; | ||
Charset charset = args.length > 3 && args[3] != null ? cast.toCharset(cast.toString(args[3])) : null; | ||
String accessKeyId = args.length > 4 && args[4] != null ? cast.toString(args[4]) : null; | ||
String secretAccessKey = args.length > 5 && args[5] != null ? cast.toString(args[5]) : null; | ||
String host = args.length > 6 && args[6] != null ? cast.toString(args[6]) : null; | ||
double timeout = args.length > 7 && !isEmpty(args[7]) ? cast.toDoubleValue(args[7]) : 0; | ||
|
||
// validate | ||
UDF targetUDF = null; | ||
Resource targetRes = null; | ||
int mode = 0; | ||
int blockSize = 0; | ||
if (target != null) { | ||
if (target instanceof UDF) { | ||
targetUDF = (UDF) target; | ||
|
||
// function return type | ||
if (!(targetUDF.getReturnType() == TYPE_ANY || targetUDF.getReturnType() == TYPE_BOOLEAN)) throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 3, | ||
"target", "the function invoke of the component listener must have the return type boolean.", ""); | ||
|
||
// function invoke arguments | ||
FunctionArgument[] udfArgs = targetUDF.getFunctionArguments(); | ||
if (udfArgs.length < 1 || udfArgs.length > 1) throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 3, "target", | ||
"you need to define an argument for the closure/function passed in following this pattern (string line|binary{number}|string{number})", ""); | ||
|
||
FunctionArgument arg = udfArgs[0]; | ||
if (!(arg.getType() == TYPE_ANY || arg.getType() == TYPE_STRING)) throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 3, "target", | ||
"the first argument of the closuere/function need to be defined as a string or no defintion at all", ""); | ||
print.e("name:" + arg.getName()); | ||
|
||
String name = (arg.getName().getString() + "").toLowerCase().trim(); | ||
if ("line".equals(name)) { | ||
mode = MODE_LINE; | ||
} | ||
else if (name.startsWith("binary")) { | ||
mode = MODE_BINARY; | ||
blockSize = eng.getCastUtil().toIntValue(name.substring(6)); | ||
if (blockSize <= 0) throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 3, "target", | ||
"invalid block size defintion with the argument [binary{Number}], blocksize need to be a positive number, so the argument name should for example look like this [binary1000] to get block size 1000", | ||
""); | ||
} | ||
else if (name.startsWith("string")) { | ||
mode = MODE_STRING; | ||
blockSize = eng.getCastUtil().toIntValue(name.substring(6)); | ||
if (blockSize <= 0) throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 3, "target", | ||
"invalid block size defintion with the argument [string{Number}], blocksize need to be a positive number, so the argument name should for example look like this [string1000] to get block size 1000", | ||
""); | ||
} | ||
else { | ||
throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 3, "target", | ||
"the first argument of the closuere/function need to be define an argument where the name does follow one of this patterns [line, binary(Number), string(Number) ]", | ||
""); | ||
} | ||
|
||
} | ||
else if ((targetRes = S3Write.toResource(pc, target, false, null)) == null) { | ||
// can also be a charset defintion | ||
Charset tmp = charset == null ? eng.getCastUtil().toCharset(eng.getCastUtil().toString(target, null), null) : null; | ||
if (tmp == null) throw eng.getExceptionUtil().createFunctionException(pc, "S3Download", 3, "target", | ||
"the value of the argument needs to be a closure/function, a file name or not defined at all", ""); | ||
charset = tmp; | ||
} | ||
} | ||
|
||
// create S3 Instance | ||
try { | ||
S3 s3 = S3.getInstance(toS3Properties(pc, accessKeyId, secretAccessKey, host), toTimeout(timeout)); | ||
S3Object obj = s3.getData(bucketName, objectName); | ||
Cast caster = eng.getCastUtil(); | ||
// stream to UDF | ||
if (targetUDF != null) { | ||
// LINE | ||
if (MODE_LINE == mode) { | ||
BufferedReader reader = null; | ||
try { | ||
if (charset == null) charset = pc.getConfig().getResourceCharset(); | ||
reader = new BufferedReader(new InputStreamReader(obj.getObjectContent(), charset)); | ||
String line; | ||
while ((line = reader.readLine()) != null) { | ||
if (!caster.toBooleanValue(targetUDF.call(pc, new Object[] { line }, true))) return null; | ||
} | ||
return null; | ||
} | ||
finally { | ||
eng.getIOUtil().closeSilent(reader); | ||
} | ||
} | ||
// STRING | ||
else if (MODE_STRING == mode) { | ||
BufferedReader reader = null; | ||
char[] buffer = new char[blockSize]; | ||
try { | ||
if (charset == null) charset = pc.getConfig().getResourceCharset(); | ||
reader = new BufferedReader(new InputStreamReader(obj.getObjectContent(), charset)); | ||
int numCharsRead; | ||
while ((numCharsRead = reader.read(buffer, 0, buffer.length)) != -1) { | ||
String block = new String(buffer, 0, numCharsRead); | ||
if (!caster.toBooleanValue(targetUDF.call(pc, new Object[] { block }, true))) return null; | ||
} | ||
return null; | ||
} | ||
finally { | ||
eng.getIOUtil().closeSilent(reader); | ||
} | ||
} | ||
// BINARY | ||
else { | ||
InputStream input = null; | ||
byte[] buffer = new byte[blockSize]; | ||
try { | ||
input = new BufferedInputStream(obj.getObjectContent()); | ||
int bytesRead; | ||
while ((bytesRead = input.read(buffer)) != -1) { | ||
if (bytesRead == buffer.length) { | ||
if (!caster.toBooleanValue(targetUDF.call(pc, new Object[] { buffer }, true))) return null; | ||
} | ||
else { | ||
// create a smaller array for the last block, which might not be a full 1000 bytes | ||
byte[] lastBlock = Arrays.copyOf(buffer, bytesRead); | ||
targetUDF.call(pc, new Object[] { lastBlock }, true); | ||
} | ||
} | ||
return null; | ||
} | ||
finally { | ||
eng.getIOUtil().closeSilent(input); | ||
} | ||
} | ||
|
||
} | ||
// store to file | ||
else if (targetRes != null) { | ||
eng.getIOUtil().copy(obj.getObjectContent(), targetRes, true); | ||
return null; | ||
} | ||
// return the value | ||
else { | ||
if (charset == null) { | ||
// TODO get mimetype info from S3 and decide based on this if we provide a byte array or a string | ||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
eng.getIOUtil().copy(obj.getObjectContent(), baos, true, true); | ||
return baos.toByteArray(); | ||
} | ||
return eng.getIOUtil().toString(obj.getObjectContent(), charset); | ||
} | ||
} | ||
catch (Exception e) { | ||
throw eng.getCastUtil().toPageException(e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.