Alfresco REST, file upload: cannot set description and filename - java

I'm using the following code to upload a file onto Alfresco
CloseableHttpClient client=HttpClients.createDefault();
HttpPost postMethod=new HttpPost(AlfrescoRequests.getUploadRequest()+"?alf_ticket="+DocumentUno.alFrescoSessionTicket.replace("\"", ""));
System.out.println(AlfrescoRequests.getUploadRequest()+"?ticket="+DocumentUno.alFrescoSessionTicket.replace("\"", ""));
MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.RFC6532);
reqEntity.addPart("filedata", new FileBody(file));
reqEntity.addPart("alt_destination",new StringBody("workspace://SpacesStore/9a37fce5-2715-4730-a245-64f161304879"));
reqEntity.addPart("filename",new StringBody("ip_VM1985.txt"));
reqEntity.addPart("description", new StringBody("Descrizione del file"));
postMethod.setEntity(reqEntity);
System.out.println("executing request " + postMethod.getRequestLine());
CloseableHttpResponse resp = client.execute(postMethod);
Where AlfrescoRequests.getUploadRequest() gives the REST request URL (query-part excluded)
public static String getUploadRequest()
{
return DocumentUno.ECM_ADDRESS+"/alfresco/service/api/upload";
}
The upload is done correctly, i get a code 200, but the description is not set and i cannot specify a filename, that remains the same of the original file. Please tell me if you need other details.

The /alfresco/service/api/upload webscript unfortunately does not handle metadata. This is a known feature of Alfresco. The filename is handled but the description, which really is the cm:description property, like any other additional standard or custom properties that you might have, are not saved.
I believe the description parameter of the webscript is used as the first parameter for this API:
ScriptNode checkin(String history, boolean majorVersion)
... and has nothing to do with the document title/description.
There is an open-source plugin (which I authored) that overcomes this limitation by allowing to specify any additional metadata during the upload:
http://softwareloop.com/uploader-plus-an-alfresco-uploader-that-prompts-for-metadata/
It comprises a repo amp and a share amp. Since you're using web services, you just need the repo amp.

Related

Is there a way to retrieve file from SharePoint without file name?

I know how to retrieve a file with a specified name like this:
HttpGet httpGet = new HttpGet(siteUrl +
"_api/web/GetFolderByServerRelativeUrl('Shared%20Documents/Working%20Files/FolderName')/Files('"
+workbookName+"')/$value");
but is there a way to retrieve it without the name? maybe by index?
Ive checked the documentation on https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/working-with-folders-and-files-with-rest but saw no other examples on how else to retrieve files
Every file you upload into the Sharepoint will have the field Id mapped to it. This ID would be returned as part of the response whenever you create the file in the Sharepoint. This response would also return several links and fields related to the particular file you uploaded, in order to perform any operations on that file.
Using these fields such as Id, E-Tag you should be able to form another request to get the file name.
You can also find the request URL in the response you received after file creation.

Parse multipart/form-data Body on AWS Lambda in Java

I am new to AWS Lambda and I am trying to implement a Lambda function that receives a POST request containing data encoded as multipart/form-data. The message is received through the API Gateway using Lambda Proxy integration and the body is encoded in Base64 when it arrives to the Lambda function. After decoding it manually, I see it contains a multipart body like the following:
-----WebKitFormBoundary3EZ0C3tbP2JpAmz4
Content-Disposition: form-data; name="param1"
value1
-----WebKitFormBoundary3EZ0C3tbP2JpAmz4
Content-Disposition: form-data; name="param2"
value2
------WebKitFormBoundary3EZ0C3tbP2JpAmz4
Content-Disposition: form-data; name="myfile"; filename="ivr.png"
Content-Type: image/png
PNG
... [binary stuff]
------WebKitFormBoundary3EZ0C3tbP2JpAmz4--
What I need is to parse this message in java 8 so I can access the individual parts. I managed to do it using the +javax.mail.Multipart+ object but it seems I cannot access the "name" property for the parts and as such I cannot distinguish between same type elements, e.g. "param1" and "param2".
I believe this can be related to the fact that this Class is for parsing email messages...
Is there another way to parse this multipart body inside the lambda function? This is the code I have to parse it (base64 is the string containing the body):
DataSource source = new ByteArrayDataSource(new ByteArrayInputStream(Base64.decodeBase64(base64)), "multipart/mixed");
MimeMultipart mp = new MimeMultipart(source);
I'd appreciate any help you can provide.
Ok so this is definitely NOT the ideal solution but I was able to make this work.
Problem
There are actually many libraries out there to parse multipart form data. The actual problem is all libraries rely on javax.servlet package - most importantly HttpServletRequest class (and few more).
Since we can't access javax.servlet package classes in AWS Lambda environment, my solution is to fix that.
Solution
Download the javax.servlet package from GitHub and add that to you your lambda function. See the image below - you can see that my class MultipartFormDataTest is within my package com... and I also have javax.servlet package within the same Java module.
Once we do this, we can use one or more libraries that will do all the work for us. The best library I've found that will parse the file content is Delight FileUpload - https://mvnrepository.com/artifact/org.javadelight/delight-fileupload.
Once that library is added, the method below getFilesFromMultipartFormData() will return ArrayList<File> where each item in the list represents a File that was sent in the request.
/**
* #param context context
* #param body this value taken from the `request.getBody()`
* #param contentType this value is taken from `request.headers().get("Content-Type")`
* #return List of File objects
*/
private List<File> getFilesFromMultipartFormData(Context context, String body, String contentType) {
ArrayList<File> files = new ArrayList<>();
List<FileItem> fileItems = FileUpload.parse(body.getBytes(StandardCharsets.UTF_8), contentType);
for(FileItem fileItem : fileItems) {
if(fileItem == null) {
continue;
}
logger.log("fileItem name: " + fileItem.getName());
logger.log("fileItem content-type: " + fileItem.getContentType());
logger.log("fileItem size: " + fileItem.getSize());
// Note: instead of storing it locally, you can also directly store it to S3 for example
try {
// I'm setting the extension to .png but you can look at the fileItem.getContentType()
// to make sure it is an image vs. pdf vs. some other format
File temp = File.createTempFile(fileItem.getName(), ".png");
Files.copy(fileItem.getInputStream(), temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
files.add(temp);
} catch (Exception e) {
continue;
}
}
return files;
}

ckan add source url using java client

I am trying to add the source URL on an existing resource but I can't get it to work.
This is what I have:
HttpEntity reqEntity = MultipartEntityBuilder.create()
.addPart("id", new StringBody("resource id",ContentType.TEXT_PLAIN))
.addPart("Source", new StringBody("www.google.com",ContentType.TEXT_PLAIN))
.build();
postRequest = new HttpPost(host+"/api/3/action/resource_update");
postRequest.setEntity(reqEntity);
postRequest.setHeader("X-CKAN-API-Key", "myApi");
Which gives me the following error:
"success": false, "error": {"__type": "Validation Error", "url": ["Missing value"]}
I can add a Source field when I upload a file, but it doesn't create a link for it. Maybe because I use ContentType.TEXT_PLAIN instead of something that will tell CKAN that this is a link?
Edit:
By source I mean the link on the resource information where it points to the webpage of the original data. When manually uploading a file you have that option, I just cant get it to work with the API.
The error is telling you that you've not specified the url value for the resource, which is necessary.
The content that you POST needs to be in JSON format, whereas you appear to be sending it as multipart form data.
The JSON keys you need to send for a resource are id, url, name, format etc. i.e. you need to set url rather than Source. You can see the existing keys by looking at /api/action/resource_show?id=xyz.
The normal way to use resource_update is to do resource_show first, edit the JSON with the new values, and then resource_update. That way you don't lose any other properties of the resource, such as format.

Render Tapestry page and get it as Stream/String resource

Is any convinient way to dynamically render some page inside application and then retrieve its contents as InputStream or String?
For example, the simplest way is:
// generate url
Link link = linkSource.createPageRenderLink("SomePageLink");
String urlAsString = link.toAbsoluteURI() + "/customParam/" + customParamValue;
// get info stream from url
HttpGet httpGet = new HttpGet(urlAsString);
httpGet.addHeader("cookie", request.getHeader("cookie"));
HttpResponse response = new DefaultHttpClient().execute(httpGet);
InputStream is = response.getEntity().getContent();
...
But it seems it must be some more easy method how to archive the same result. Any ideas?
I created tapestry-offline for exactly this purpose. Please be aware of the issue here (workaround included).
It's probably best to understand your exact use case. If, for example, you are generating emails in a scheduled task, it's probably better to configure jenkins or cron to hit a URL.
It's probably also worth mentioning the capture component from tapestry-stitch
This is only useful in situations where you want to cature part of a page as a String during page / component render.

Jersey Post request - How to perform a file upload with an unknown number of additional parameters?

I asked something like this previously, but upon re-reading my original post, it was not easy to understand what I was really asking. I have the following situation. We have (or at least I'm trying to get working) a custom file upload procedure that will take in the file, a set number of 'known' metadata values (and they will always be there), as well as potentially an unknown number of additional metadata values. The service that exists currently uses the Jersey framework (1.16)
I currently have both client and server code that handles dealing with the file upload portion and the known metadata values (server code below)
#POST
#Path("asset/{obfuscatedValue0}/")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public UUID uploadBlob(#PathParam("obfuscatedValue0") Integer obfuscatedValue0,
#FormDataParam("obfuscatedValue1") String obfuscatedValue1,
#FormDataParam("obfuscatedValue2") String obfuscatedValue2,
#FormDataParam("obfuscatedValue3") String obfuscatedValue3,
#FormDataParam("obfuscatedValue4") String obfuscatedValue4,
#FormDataParam("obfuscatedValue5") String obfuscatedValue5,
#FormDataParam("file") InputStream uploadedInputStream) {
.....
}
...and excerpt of client code:
Builder requestBuilder = _storageService
.path("asset")
.path(obfuscatedValue0.toString())
.type(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.APPLICATION_JSON);
FormDataMultiPart part = new FormDataMultiPart()
.field("file", is, MediaType.TEXT_PLAIN_TYPE) // 'is' is an inputstream from earlier in code.
.field("obfuscatedValue1", obfuscatedValue1)
.field("obfuscatedValue2", obfuscatedValue2)
.field("obfuscatedValue3", obfuscatedValue3)
.field("obfuscatedValue4", obfuscatedValue4)
.field("obfuscatedValue5", obfuscatedValue5);
storedAsset = requestBuilder.post(UUID.class, part);
However, I need to pass a map of additional parameters that will have an unknown number of values/names. From what I've seen, there is no easy way to do this using the FormDataParam annotation like my previous example.
Based upon various internet searches related to Jersey file uploads, I've attempted to convert it to use MultivaluedMap with the content type set to "application/x-www-form-urlencoded" so it resembles this:
#POST
#Path("asset/{value}/")
#Consumes("application/x-www-form-urlencoded")
public UUID uploadBlob(#PathParam(value), MultivaluedMap<String,String> formParams) {
....
}
It's my understanding that MultivaluedMap is intended to obtain a general map of form parameters (and as such, cannot play nicely together in the same method bearing #FormDataParam annotations.) If I can pass all this information from the Client inside some sort of map, I think I can figure out how to handle parsing the map to grab and 'doMagic()' on the data to get what I want done; I don't think I'll have a problem there.
What I AM fairly confused about is how to format the request client-side code when using this second method within the jersey framework. Can anyone provide some guidance for the situation, or some suggestions on how to proceed? I'm considering trying the solution proposed here and developing a custom xml adapter to deal with this situation, and sending xml instead of multipart-form-data but I'm still confused how this would interact with the InputStream value that will need to be passed. It appears the examples with MultivaluedMap that I've seen only deal with String data.

Categories