How to create custom file names for S3 objects using Java - java

I am using Spring Boot, Snowflake and AWS S3.
I have SQL query that queries two tables and gets the result. That result I have to write to S3 like CSV file and get URL for download in return.
I am doing that by creating temporary table and deleting it after data is copied to S3.
Here is code I have:
#Override
public void getUserTest(String userId) {
String q = "CREATE TEMPORARY TABLE \"TEST\".\"PUBLIC\".\"USER_TABLE_TEMP\" AS SELECT \"ID\", \"FIRST_NAME\", \"LAST_NAME\" from \"TEST\".\"PUBLIC\".\"USER_TABLE\"\n" +
" where \"ID\" = ?\n" +
" union all\n" +
" select \"ID\",\"ACCOUNT_NAME\", \"ACCOUNT_NUMBER\" from \"TEST\".\"PUBLIC\".\"ACCOUNT_DATA\"\n" +
" where \"ID\" = ?";
jdbcTemplate.query(q, s -> {}, userId, userId);
}
Method that writes to S3.
#Override
public URL writeToS3() {
String q = "copy into s3://snowflake171 from \"TEST\".\"PUBLIC\".\"USER_TABLE_TEMP\" storage_integration = s3_int file_format = CSV_TEST;\n";
jdbcTemplate.query(q, s -> {});
URL url = generateURL();
String dropTable = "drop table if exists \"TEST\".\"PUBLIC\".\"USER_TABLE_TEMP\"";
jdbcTemplate.query(dropTable, s -> {});
return url;
}
Method that generated URL:
public URL generateURL() {
try {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
final AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withCredentials(new
AWSStaticCredentialsProvider(awsCreds)).withRegion(clientRegion).build();
// Set the presigned URL to expire after 2h.
java.util.Date expiration = new java.util.Date();
long expTimeMillis = Instant.now().toEpochMilli();
expTimeMillis += 1000 * 60 * 120;
expiration.setTime(expTimeMillis);
// Generate the presigned URL.
System.out.println("Generating pre-signed URL.");
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(bucket, objectKey)
.withMethod(HttpMethod.GET)
.withExpiration(expiration);
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
System.out.println("Pre-Signed URL: " + url.toString());
return url;
} catch (AmazonServiceException e) {
// The call was transmitted successfully, but Amazon S3 couldn't process
// it, so it returned an error response.
e.printStackTrace();
} catch (SdkClientException e) {
// Amazon S3 couldn't be contacted for a response, or the client
// couldn't parse the response from Amazon S3.
e.printStackTrace();
}
return null;
}
Data is queried by userId I sent in. Everything works fine, but I am generating every time file with same name. And if I dont delete existing file in S3 I am not able to upload new one.
I should be able to upload different files for different userId.
How can I do this?
How can I give different names from files created in S3?
I have seen this in docs https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html but I don't know the best way to apply in code.
Is there a way I can add userId as prefix to filename?

You can add a custom object key with userId in your COPY query :
#Override
public URL writeToS3(userId) {
String q = "copy into s3://snowflake171/" + userId + " from \"TEST\".\"PUBLIC\".\"USER_TABLE_TEMP\" storage_integration = s3_int file_format = CSV_TEST;\n";
jdbcTemplate.query(q, s -> {});

Related

Amazon Dynamodb ConditionalCheckFailedException throws java InaccessibleObjectException

I am using updateItem based on some condition. It works fine if condition is met, but it throws ConditionalCheckFailedException along with java InaccessibleObjectException if condition fails.
Why is it throwing InaccessibleObjectException? Also, how to handle it?
Update: Error also occurs in case of ValidationException
updateItemSpec = new UpdateItemSpec()
.withConditionExpression()
table.update(updateItemSpec).getItem()
You are using an old Java API for Amazon DynamoDB. To update a table, consider moving away from V1 and use the Enhanced Client - which is part of the AWS SDK for Java V2. More information here:
Mapping items in DynamoDB tables
Here is the code to update a table using the Enhanced Client.
public class EnhancedModifyItem {
public static void main(String[] args) {
String usage = "Usage:\n" +
" UpdateItem <key> <email> \n\n" +
"Where:\n" +
" key - the name of the key in the table (id120).\n" +
" email - the value of the modified email column.\n" ;
if (args.length != 2) {
System.out.println(usage);
System.exit(1);
}
String key = args[0];
String email = args[1];
Region region = Region.US_EAST_1;
DynamoDbClient ddb = DynamoDbClient.builder()
.region(region)
.build();
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
.dynamoDbClient(ddb)
.build();
String updatedValue = modifyItem(enhancedClient,key,email);
System.out.println("The updated name value is "+updatedValue);
ddb.close();
}
public static String modifyItem(DynamoDbEnhancedClient enhancedClient, String keyVal, String email) {
try {
//Create a DynamoDbTable object
DynamoDbTable<Customer> mappedTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
//Create a KEY object
Key key = Key.builder()
.partitionValue(keyVal)
.build();
// Get the item by using the key and update the email value.
Customer customerRec = mappedTable.getItem(r->r.key(key));
customerRec.setEmail(email);
mappedTable.updateItem(customerRec);
return customerRec.getEmail();
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
System.exit(1);
}
return "";
}
}
You can find all V2 DynamoDB examples here.

How can i get an object like OLAPDataSetInterface in SAS 9.4

I need get an object like OLAPDataSetInterface but without using an OLAP server.
I used to use:
//Connect to OLAP server
OLAPDataSetInterface cube = objectInSession.getOlapDataSet();
//Get the info
cube.execute("query_mdx");
I tried use DataSetInterface but doesnt work:
com.sas.sasserver.dataset.DataSetInterface ds = null;
//Getting my temporary table
ds.setDataSet("WORK.my_table");
And i do the following:
//BBDD connector
WorkspaceConnector connector = factory.getWorkspaceConnector(0L);
IWorkspace workspace = connector.getWorkspace();
ILanguageService ls = workspace.LanguageService();
//This creates my temporary table in the library WORK (WORK.my_table)
String stmt = "%include \"/saswork/MY_PROGRAM.sas\" ;";
ls.Submit(stmt);
com.sas.sasserver.dataset.DataSetInterface ds = null;
//ds = ...
This is in C# but hould help understand the dataset retrieval process. I don't work with SAS OLAP so cannot tellyou how to work that item.
public string GetDataSet(string sasDirectory, string dataset)
{
Common.Log($"Getting SAS dataset ({dataset}) at {sasDirectory}");
DataTable dt = new DataTable(dataset);
try
{
using (var cn = new OleDbConnection($#"Provider=SAS.LocalProvider; Data Source={sasDirectory}"))
{
cn.Open();
var cmd = cn.CreateCommand();
cmd.CommandType = CommandType.TableDirect;
cmd.CommandText = dataset;
var sas = new OleDbDataAdapter(cmd);
var ds = new System.Data.DataSet();
sas.Fill(ds, dataset);
dt = ds.Tables[0];
Common.Log($"SAS dataset loaded.");
}
}
catch (Exception ex)
{
string errMessage = "Unable to get the SAS dataset. Library: " + sasDirectory + ", DataSet: " + dataset + ", " +
ex.TargetSite.Name;
Common.Log($"SAS Error in {MethodBase.GetCurrentMethod().Name}", MessageType.Error, ex);
}
return dt.ToCsv('\t');
}

NotesException: A required argument has not been provided

My XPage gathers information which I use to populate a document in a different Domino database. I use a link button (so I can open another XPage after submission). The onClick code is as follows:
var rtn = true
var util = new utilities()
var hostURL = configBean.getValue("HostURL");
var userAttachment;
//set up info needed for checking duplicates
var attachName=getComponent("attachmentIdentifier").getValue();
var serialNbr = getComponent("serialNumber").getValue();
userAttachment = user+"~"+attachName;
var userSerial = user+"~"+serialNbr;
//Done setting info needed
//check for duplicates
rtn = utilBean.checkAttachmentName(userAttachment, userSerial)
//done
if(rtn==true){
var doc:Document = document1;
dBar.info("ALL IS GOOD");
var noteID:String=document1.getNoteID();
dBar.info("Calling saveNewAttachment using NoteID " + noteID )
rtn=utilBean.saveNewAttachment(session,noteID ); //<<< I get error here
dBar.info("rtn = " + rtn)
return "xsp-success";
view.postScript("window.open('"+sessionScope.nextURL+"')")
}else if (rtn==false){
errMsgArray = utilBean.getErrorMessages();
for(err in errMsgArray){
//for (i=0; i < errMsgArray.size(); i++){
dBar.info("err: "+ err.toString());
if (err== "nameUsed"){
//send message to XPXage
facesContext.addMessage(attachmentIdentifier.getClientId(facesContext) , msg(langBean.getValue("duplicateName")));
}
if(err=="serialUsed"){
//send message to XPXage
facesContext.addMessage(serialNumber.getClientId(facesContext) , msg(langBean.getValue("duplicateSerial")));
}
}
return "xsp-failure";
}
And the java code that delivers the error is this
public boolean saveNewAttachment(Session ses, String noteID)
throws NotesException {
debugMsg("Entering saveNewAttachment and NOTEID = "+noteID);
// this is used when the user saves an attachment to to the
// user profiles db
boolean rtn = false;
Document doc;
ConfigBean configBean = (ConfigBean)
ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(),
"configBean");
String dbName = (String) configBean.getValue("WebsiteDbPath");
debugMsg("A");
Database thisDB = ses.getDatabase(ses.getServerName(), dbName, false);
String value;
try {
debugMsg("noteID: "+noteID);
The next line throws the NotesException error
doc = thisDB.getDocumentByID("noteID");
debugMsg("C");
} catch (Exception e) {
debugMsg("utilitiesBean.saveAttachment: " + e.toString());
e.printStackTrace();
System.out.println("utilitiesBean.saveAttachment: " + e.toString());
throw new RuntimeException("utilitiesBean.saveAttachment: "
+ e.toString());
}
return rtn;
}
I might be going about this wrong. I want to save the document which the data is bound to the User Profile database but if I submit it I need to redirect it to a different page. That is why I am using a link, however, I am having a hard time trying to get the document saved.
Has document1 been saved before this code is called? If not, it's not in the backend database to retrieve via getDocumentByID().
I'm assuming this line has been copied into here incorrectly, because "noteID" is not a NoteID or a variable holding a NoteID, it's a string.
doc = thisDB.getDocumentByID("noteID");

List all attachments stored in cloudant with java

I'm developing a demo and I'm stuck with this.
I want to list in a java web app all the attachments (PDFs for example), but a I am not able to retrieve and list them.
I'm only able to retrieve common data (String, Ints).
Is there a standard way to retrieve and show ?
I been reading all the posts but nothing seems to work.
Here is where I add the vendor, with the attachment:
public void addVendor(final Vendor vendor, final InputStream inputStream, final long size, final String contentType)
{
final Database db = getDb();
final int id = Integer.valueOf(vendor.get_id()) + 1;
final Response r1 = db.saveAttachment(inputStream, vendor.getName() + ".txt", contentType, String.valueOf(id), null);
vendor.setAttachment(r1);
final Response r = db.post(vendor);
System.out.println("Vendor created successfully. Id: " + r.getId() + ", rev: " + r.getRev());
System.out.println("File created successfully. Id: " + r1.getId() + ", rev: " + r1.getRev());
}
Here I where I try to retrive the data:
public List<Vendor> getAllVendors()
{
List<Vendor> Vendors = new ArrayList<Vendor>();
final List<Vendor> vend2 = new ArrayList<Vendor>();
//Get db
final Database db = getDb();
final InputStream s = null;
//Get all documents
Vendors = db.view("_all_docs").includeDocs(true).query(Vendor.class);
final Database db1 = getDb();
for (final Vendor vend : Vendors) {
final Response r1 = vend.getAttachment();
final int id = Integer.valueOf(vend.get_id()) + 1;
// Here I am look to the attachment with the _ID and _REV
final InputStream in = db1.find(r1.getId(), r1.getRev()); vend.setInput(in); vend2.add(vend);
}
return Vendors;
}
I this last code, I intended to create a new list with all my Vendor data plus the blob.
When I add the vendor ( in the first part ) , I saved the " response " of the attachement in the vendor object, SO when I tried to retrive I have the data to work with ( _id and _rev ) .
I'm assuming you want to list all documents that contain attachments. If so, you can create a MapReduce view similar to this:
function(doc) {
if (doc._attachments) {
emit(doc._id, null);
}
}
You would then call the view using something like this to get a list of document ids of documents that contain attachments:
GET /dbname/_design/designdocname/_view/docswithattachments
The above GET request would look something like this in Java:
List<Foo> list = db.view("designdocname/docswithattachments")
.query(Foo.class);

Delete files, directories and buckets in amazon s3 java

I'm wondering how to do this. I looked at the sdk documentation and have some examples, but am confused how the syntax generally goes.
If I want to delete a file, I assume I use deleteObject(path, key). However, what is the "key"?
Also how do you delete a directory? I can't seem to find a method for doing that.
This snippet of code works for me. folderPath is something like "topDir/secondDir/"
void deleteObjectsInFolder(String bucketName, String folderPath) {
for (S3ObjectSummary file : s3.listObjects(bucketName, folderPath).getObjectSummaries()){
s3.deleteObject(bucketName, file.getKey());
}
}
A "key" in S3 is similar to a file path:
http://bucket.s3.amazonaws.com/some/path/to/use
... is in a bucket named bucket and has a key of some/path/to/use.
It's not actually a path though, because there are no folders. The S3 key is just the file name for a file in one big directory (the entire bucket). S3 keys can contain /, but it has no special meaning unless you set the delimiter argument with listing a bucket.
In other words, having an object named some/object doesn't tell you anything about the object some (it might or might not exist -- the two objects are not related).
However, you can request keys with a specific prefix, so I could say "give me all keys starting with some/path/to/ and it will return some/path/to/use. It looks like "listing a directory", but it's really just asking for files that start with a specific string of characters.
I could just as easily name things like this:
somepathtousea
somepathtouseb
And say "give me everything starting with somepathtouse" (and it would say somepathtousea and somepathtouseb).
Note: S3 URL's come in several forms:
http://s3.amazonaws.com/bucket/key
http://bucket.s3.amazonaws.com/key
http://bucket/key (where bucket is a DNS CNAME record pointing to bucket.s3.amazonaws.com)
EDIT:
I looked at the JavaDocs and this is the function signature I see (for AmazonS3Client):
public void deleteObject(java.lang.String bucketName,
java.lang.String key)
throws AmazonClientException,
AmazonServiceException
EDIT again:
Folders do kind-of exist now, as zero-length objects with a content-type of application/x-directory and a key ending in /:
$ AWS_PROFILE=prod aws s3api head-object --bucket example-bucket --key example-directory/
{
"AcceptRanges": "bytes",
"LastModified": "Mon, 29 Apr 2019 14:59:36 GMT",
"ContentLength": 0,
"ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
"ContentType": "application/x-directory",
"ServerSideEncryption": "AES256",
"Metadata": {}
}
This is still just convention and there's nothing stopping you from having files ending / or files inside of "folders" that don't exist.
You might want to take a look at this example for a quick reference on how you can delete objects from S3.
The syntax for delete is actually
deleteObject( bucketName, key )
where bucketName is the bucket in which you have placed your files and key is name of the file you want to delete within the bucket.
Think of a bucket as your hard disk drive like C:\ , D:\ etc. And key as the absolute pathname of a file you want to delete.
/*Here is solution that works for me. Here Bucket_Name is my bucket name on S3, and key is the path under Bucket_Name. So, if absolute path on S3 is:
s3://my_bucket/Path/to/my/folder
then, the code below should work. */
String Bucket_Name = "my_bucket";
String key = "Path/to/my/folder";
ObjectListing objects = s3Client.listObjects(BUCKET_NAME, key);
for (S3ObjectSummary objectSummary : objects.getObjectSummaries())
{
s3Client.deleteObject(BUCKET_NAME, objectSummary.getKey());
}
As question is asking about Deleting files, directories and buckets in amazon S3 java, I would like to offer code for deleting a non-empty S3 bucket (AWS Reference):
public void deleteBucket(final String bucketName) {
final AmazonS3 s3 = AmazonS3ClientBuilder.defaultClient();
try {
ObjectListing objectListing = s3.listObjects(bucketName);
while (true) {
for (Iterator<?> iterator = objectListing.getObjectSummaries().iterator(); iterator.hasNext(); ) {
S3ObjectSummary summary = (S3ObjectSummary) iterator.next();
s3.deleteObject(bucketName, summary.getKey());
}
if (objectListing.isTruncated()) {
objectListing = s3.listNextBatchOfObjects(objectListing);
} else {
break;
}
}
VersionListing versionListing = s3.listVersions(new ListVersionsRequest().withBucketName(bucketName));
while (true) {
for (Iterator<?> iterator = versionListing.getVersionSummaries().iterator(); iterator.hasNext(); ) {
S3VersionSummary vs = (S3VersionSummary) iterator.next();
s3.deleteVersion(bucketName, vs.getKey(), vs.getVersionId());
}
if (versionListing.isTruncated()) {
versionListing = s3.listNextBatchOfVersions(versionListing);
} else {
break;
}
}
s3.deleteBucket(bucketName);
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
}
}
Works for me, beware of truncation!
long start = System.currentTimeMillis();
long totalSize = 0;
int totalItems = 0;
String key ="path/to/folder/"
String bucket = "my-bucket"
final ListObjectsRequest listObjectsRequest = new ListObjectsRequest().withBucketName(bucketName).withPrefix(key);
ObjectListing objects = s3.listObjects(listObjectsRequest);
do {
for (S3ObjectSummary objectSummary : objects.getObjectSummaries()) {
totalSize += objectSummary.getSize();
totalItems++;
s3.deleteObject(bucketName, objectSummary.getKey());
}
objects = s3.listNextBatchOfObjects(objects);
} while (objects.isTruncated());
long stop = System.currentTimeMillis();
LOG.trace("User {} had {} items with {} Kb, took {} ms to delete", user.getName(), totalItems, totalSize / 1024, stop
- start);
The ListObjectsV2Result worked for me. Try once.
private void deleteObjectsInFolder() {
try {
ListObjectsV2Result result;
do {
String folderPath = " ";
result = s3.listObjectsV2(Constants.BUCKET_NAME, folderPath);
Log.e("count:", result.getKeyCount() + "");
if (result.getKeyCount() != 0) {
for (S3ObjectSummary objectSummary :
result.getObjectSummaries()) {
s3.deleteObject(Constants.BUCKET_NAME, objectSummary.getKey());
}
}
System.out.println("Next Continuation Token : " + result.getNextContinuationToken());
} while (result.isTruncated() == true);
} catch (AmazonServiceException ase) {
System.out.println("Caught an AmazonServiceException, " +
"which means your request made it " +
"to Amazon S3, but was rejected with an error response " +
"for some reason.");
System.out.println("Error Message: " + ase.getMessage());
System.out.println("HTTP Status Code: " + ase.getStatusCode());
System.out.println("AWS Error Code: " + ase.getErrorCode());
System.out.println("Error Type: " + ase.getErrorType());
System.out.println("Request ID: " + ase.getRequestId());
} catch (AmazonClientException ace) {
System.out.println("Caught an AmazonClientException, " +
"which means the client encountered " +
"an internal error while trying to communicate" +
" with S3, " +
"such as not being able to access the network.");
System.out.println("Error Message: " + ace.getMessage());
}
}
Deleting a list of objects from S3 bucket by bulks:
public void deleteObjects(String bucketName, List<String> keys) {
List<KeyVersion> bulk = new ArrayList<>();
for (int i = 0; i < keys.size(); i++) {
bulk.add(new KeyVersion(keys.get(i)));
if (i % 100 == 0) {
try {
s3Client.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(bulk));
} catch (Exception e) {
System.err.println(e.getErrorMessage());
}
bulk.clear();
}
}
if (bulk.size() > 0) {
try {
s3Client.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(bulk));
} catch (Exception e) {
System.err.println(e.getErrorMessage());
}
}
}
Source: http://codeflex.co/delete-objects-from-amazon-s3-bucket-using-aws-sdk-for-java/
This line of code works in my case where the keyName is the file name:
s3Client.deleteObject(new DeleteObjectRequest(bucketName, keyName));
kotlin
class S3(
var bucketName: String? = null,
var key: String? = null,
val accessKey: String? = null,
val secretKey: String? = null,
val region: String? = null
)
fun delete(
s3: S3,
keyword: String = "",
) {
with(s3) {
val client = client(accessKey, secretKey, region)
var objects = client.listObjects(bucketName, key)
while (true) {
for (i in objects.objectSummaries) {
if (!i.key.contains(keyword)) {
continue
}
client.deleteObject(bucketName, i.key)
}
if (objects.isTruncated) {
objects = client.listNextBatchOfObjects(objects)
} else {
break
}
}
var versions = client.listVersions(bucketName, key)
while (true) {
for (i in versions.versionSummaries) {
if (!i.key.contains(keyword)) {
continue
}
client.deleteVersion(bucketName, i.key, i.versionId)
}
if (versions.isTruncated) {
versions = client.listNextBatchOfVersions(versions)
} else {
break
}
}
}
}

Categories