I have an Custom Document Library Action to Alfresco files, and when I press this button opens a new page with an applet (javascript) to make changes to a file, but I'm doing the modifications in base64 and to "appear" on the screen with this :
var stringPDF = "<object data=\"data:application/pdf;base64," +
JSON.parse(pdfbase64).message + "\"
type=\"application/pdf\"width=\"100%\"
height=\"100%\"></object>";$("#pdfTexto").html(stringPDF);
But I really need is to change the file, for when the repository again, there have to change, not just display. How do I change the existing file's contents to the new with the change?
I use this URL to make GET of the file:
http://localhost:8080/share/proxy/alfresco/slingshot/node/content/workspace/SpacesStore/21384098-19dc-4d3f-bcc1-9fdc647c05dc/latexexemplo.pdf
Then I convert to the base64... And I make the changes...
But if I want to make a POST to change the content, how can I make this?
Thanks in advance.
As I mentionned in my response to this question :
The fastest and easiest way to achieve that is to leverage the RESTfull API
This will also ensure compatibility with new versions of alfresco.
Note that you need to provide the noderef for the document to update in the form property updatenoderef and that the property majorversion is a boolean flag to specify if the new version is a minor/major version of the document.
Here is a sample code that might help you with your usecase:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost uploadFile = new HttpPost(<alfresco-service-uri>+"/api/upload?alf_ticket="+<al-ticket>);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("username", "<username>", ContentType.TEXT_PLAIN);
builder.addTextBody("updatenoderef", <noderef>, ContentType.TEXT_PLAIN);
builder.addTextBody("...", "...", ContentType.TEXT_PLAIN);
builder.addBinaryBody("filedata", <InputStream>, ContentType.DEFAULT_BINARY, <filename>);
HttpEntity multipart = builder.build();
uploadFile.setEntity(multipart);
CloseableHttpResponse response = httpClient.execute(uploadFile);
String responseString = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
JSONObject responseJson = new JSONObject(responseString);
if (response.getStatusLine().getStatusCode()!=200){
throw new Exception("Couldn't upload file to the repository, webscript response :" + responseString );
}
Note 1: You need to replace these tockens <*> with your own values/vars
Note 2: If you have problem retrieving a ticket, check this link, or this one
Note 3: To do this in JavaScript instead of java, visit this link and try to use js to post the parameters I referred as instructed !
Note 4: Since you are working on share, you are most probably authenticated.
If it is the case, you can access your alfresco repo through the proxy endpoint in share and all requests will have authentication ticket attached to them before getting forwarded to your repo !
In other terms, use this endpoint :
/share/proxy/alfresco/api/upload
Instead of :
/alfresco/service/api/upload
and You won't even have to attach a ticket to your requests.
You need to follow these steps to achieve what you are looking for.
1) Reading File:
To display content of PDF file already uploaded you need to read content of file. You are able to do it successfully using following API call.
http://localhost:8080/share/proxy/alfresco/slingshot/node/content/workspace/SpacesStore/21384098-19dc-4d3f-bcc1-9fdc647c05dc/latexexemplo.pdf
2) Capture New Content:
Capture new file content from User from applet. I guess you are storing it in some String variable.
3) Edit Existing File Content:
Issue here is that you cannot simply edit any pdf file using any of out of box Alfresco REST API (as far as I know). So you need to create your own RESTFul API which could edit pdf file's content. You can consider using some third party libraries to do this job. You need to plugin logic of editing pdf in RESTFul API
4) Changes back to Repo:
Call Your API from Step 3:
You could also have look at this plugins which could fulfill your requirements.
https://addons.alfresco.com/addons/alfresco-pdf-toolkit
Hope this helps.
Related
I use an html table where it's content can be changed with mouse drag and drop implemented. Technically, you can move the data from any table cell to another. The table size 50 row * 10 column with each cell given a unique identifier. I want to export it to .xlsx format with C# EPPlus library, and give back the exported file to client.
So I need the pass the whole table data upon a button press and post it to either a web api or an mvc controller, create an excel file (like the original html table data) and send it back to download with browser.
So the idea is to create an array which contains each of table cell's value ( of course there should be empty cells in that array), and post that array to controller.
The problem with that approach lies in the download, if I call the api or mvc controller with regular jquery's ajax.post it did not recognize the response as a file.
C# code after ajax post:
[HttpPost]
public IHttpActionResult PostSavedReportExcel([FromBody]List<SavedReports> savedReports, [FromUri] string dateid)
{
//some excel creation code
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(new MemoryStream(package.GetAsByteArray()))
};
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = dateid + "_report.xlsx"
};
ResponseMessageResult responseMessageResult = ResponseMessage(response);
return responseMessageResult;
}
Usually, for this kind of result I could use window.location = myurltocontroller to download properly , but that is only for GET requests, POST anything is not possible.
I found some answers which could help me in this topic:
JavaScript post request like a form submit
This points out I should go with creating a form, which passes the values, but I do not know how to do so in case of arrays (the table consists 50*10 = 500 values which I have to pass in the form)
I tried some only frontend solutions to the html-excel export problem, which of course does not require to build files on api side, but free jquery add-ins are deprecated, not customizeable, handle only .xls formats, etc.
I found EPPlus nuget package a highly customizeable tool, that is why I want to try this is at first place.
So the question is: how can I post an array of 500 elements, that the controller will recognize, generate the file, and make it automatically download from browser?
If you can provide some code that would be fantastic, but giving me the right direction is also helpful.
Thank you.
You can use fetch() (docs) to send the request from the JS frontend. When the browser (JS) has received the response, it can then offer its binary content as a download. Something like this:
fetch("http://your-api/convert-to-excel", // Send the POST request to the Backend
{
method:"POST",
body: JSON.stringify(
[[1,2],[3,4]] // Here you can put your matrix
)
})
.then(response => response.blob())
.then(blob => {
// Put the response BLOB into a virtual download from JS
if (navigator.appVersion.toString().indexOf('.NET') > 0) {
window.navigator.msSaveBlob(blob, "my-excel-export.xlsx");
} else {
var a = window.document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = "my-excel-export.xlsx";
a.click();
}});
So the JS part of the browser actually first downloads the file behind the scenes, and only when it's done, it's triggering the "download" from the browsers memory into a file on the HD.
This is a quite common scenario with REST APIs that require bearer token authentication.
I am working with two Express JS applications one is an API and second is application that is using this API by making requests and displaying received informations to user.
In API route I'm sending image as response:
router.get('/:customer_id',authController.isAuthenticated,(req,res) => {
.
. Retrieving customer data
.
return res.sendFile('/uploads/'+foundCustomer.doc_path);
});
And later another application is getting this document:
router.get('/:customer_id',(req,res) => {
var options = {
url: 'http://'+config.API.user+':'+config.API.password+'#'+config.API.host+':'+config.API.port+'/customers/'+req.params.customer_id
};
request(options,(err,response,body)=>{
return res.render('customer/show',{
document: ?, // Send document as parameter to view
});
});
});
In this point I want to render customer/show(EJS view engine) with customer document, but I don't want to save this document in my application files, because document is only needed to display in view (customer details and document are stored in another application).
I was trying to create temporary directory in my application structure, but it is difficult to manage deleting those not needed documents (Application has many users and at the same time many customers can be displayed).
Another solution that I was trying to implement is to make Ajax request on client side and latter append received document to <object data='document'>. But this request has to be authenticated with user and password, so I realised that storing credentials on client side javascript is not the best idea...
I am not sure that is it even possible to render and display image without saving in application files?
I would be grateful for any help, maybe the best workaround is to somehow manage temporarily saved documents.
Why not create a File object inside EJS template then use that for src attribute on an <img> ? You're already getting the raw buffer/blob from your image API server. Store it inside template.
From https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
// place this code (store this variable) inside of your EJS template
// so it can be used by the client-side JS
var aBlob = new Blob( array[, options]); // Where array is the raw buffer data returned from your image API server
See https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
var objectURL = URL.createObjectURL( aBlob ); // Where object is a Blob object
See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/srcObject
const img = document.createElement('img');
img.src = objectURL;
Final solution (tested), using axios to make API request:
In my route I'm going to make HTTP request to my API to retrieve PDF file(document):
axios.get(`http://my-api/customer/id`).then(response => {
var photo = new Buffer(response.data, 'binary').toString('base64');
return res.render('customers/show',{
document: document
});
});
In my ejs view, I'm using HTML object tag to display received PDF:
<object data="data:application/pdf;base64,<%-document%>"></object>
I need to create a file and share it for all user in the same domain within a JS application.
Here's the creation request body :
{title: documentName, mimeType: "application/vnd.google-apps.spreadsheet"}
Than I add domain permissions for created file.
Permissions' insert request body :
{ "role": "writer","type": "domain","value": domain}
Result in Drive:
Everything works fine for the user that created the file, I can find the file with {q:"title='"+documentName+"' and trashed=false"}.
But I can't find that file with another user.
I tried to look in sharedWithMe folder :
{q:"title='"+that._documentName+"' and trashed=false and sharedWithMe=true"} - no results
I basically need to search like :
But I can't find anything related to source:domain in Drive API's.
I would try the following: set the owner of the file and remove sharedWithMe (after all it's explicitly shared with the domain, not with a particular user):
{q:"title='"+that._documentName+"' and '" + owner + "' in owners and trashed=false"}
I case someone still looking for this, ill post the answer.
The solution was quite simple, just needed to add query parameter corpus set to DOMAIN, something like this : {q:"title='"+that._documentName+"' and trashed=false", corpus:"DOMAIN"}
I'm developing a REST API with Play 2 and I'm wondering how to implement file upload functionality.
I've read the official Play documentation but it just provides a multipart/form-data example, while my backend does not provide any form... it just consists of a REST API to be invoked by a JavaScript client or whatever else.
That said, what's the correct way to implement such an API? Should I implement a PartHandler and then still use the mutipartFormData parser? How should I pass the file content to the API? Is there any exhaustive example on this topic?
Any help would be really appreciated.
You should look into BodyParsers: http://www.playframework.com/documentation/2.2.x/ScalaBodyParsers
What you are trying to do is not especially complicated, especially if you are only handling smaller files that would fit in memory. After all uploading a file is just about sending the file as a body of a POST or something like that. It is not any different from receiving some XML or JSON in a request.
Hope this helps
import org.apache.http.entity.mime._
import java.io.File
import org.apache.http.entity.mime.content._
import java.io.ByteArrayOutputStream
import play.api.libs.ws.WS
val contents ="contents string"
val file = File.createTempFile("sample", ".txt")
val bw = new java.io.BufferedWriter(new java.io.FileWriter(file)
bw.write(new_contents);
bw.close();
builder.addPart("file", new FileBody(file, org.apache.http.entity.ContentType.create("text/plain"), "sample"))
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
val entity = builder.build
val outputstream = new ByteArrayOutputStream
entity.writeTo(outputstream)
val header = (entity.getContentType.getName -> entity.getContentType.getValue)
val response = WS.url("/post/file").withHeaders(header).post(outputstream.toByteArray())
To pass your contents, depending on your client side, you can encode the contents to Base64 at client side to pass the contents as Json (You can use Json body parser). Then on the server side you can decode the contents using a Base64 decoder (e.g. Apache Commons) to get the byte array. It will be as simple as
Base64.decodeBase64(YourEncodedFileContent)
When you have the byte array you can simply write it on disk or save it into database etc. We are using this approach in production and it works fine however we only handle small file uploads.
OK, thank you all for your suggestions... here below is how I solved my issue:
object Files extends Controller {
def upload = SecuredAction[Files.type]("upload").async(parse.multipartFormData(partHandler)) { implicit request =>
future { request.body.files.head.ref match {
case Some((data, fileName, contentType)) => Ok(success(Json.obj("fileName" -> fileName)))
case _ => BadRequest
}}.recover { case e =>
InternalServerError(error(errorProcessingRequest(e.getMessage)))
}
}
...
private def partHandler = {
parse.Multipart.handleFilePart {
case parse.Multipart.FileInfo(partName, fileName, contentType) =>
Iteratee.fold[Array[Byte], ByteArrayOutputStream](
new ByteArrayOutputStream
) { (outputStream, data) =>
outputStream.write(data)
outputStream
}.map { outputStream =>
outputStream.close()
Some(outputStream.toByteArray, fileName, contentType.get)
}
}
}
}
I hope it helps.
while my backend does not provide any form... it just consists of a REST API to be invoked by a JavaScript client
Then your backend is not a REST API. You should follow the HATEOAS principle, so you should respond with links and forms along with data to every GET request. You don't have to send back HTML, you can describe these things with hypermedia json or xml media types, for example with JSON-LD, HAL+JSON, ATOM+XML, etc... So you have to describe your upload form in your preferred hypermedia, and let the REST client to turn that description into a real HTML file upload form (if the client is HTML). After that you can send a multipart/form-data as usual (REST is media type agnostic, so you can send data in any media type you want, not just in a JSON format). Check the AJAX file upload techniques for further detail...
I've an object created in javacript with a lot of data and I serialize it to JSON to send it to the server. After this, the server must do somework and create a dynamic file, so it can be downloaded.
For the last routine I created an ASHX but can be modified. Already I'm getting a "httpcontext" that I found in another question how to work with it to get the data from the JSON, so my question is not related about this.
The problem (more oriented to JS) is this one:
How can I sent the JSON to the ASHX as a URL/GET/POST to the generic handler to avoid the "ajax reply" and be the user open a new window with the link dinamically generated?
Thanks, sorry for my english (please edit) and kind regards!
Note 1: I can't use third-part code
Note 2: I can't use JSON.NET
Note 3: I can't save the report on the server so the response must be a generated file to download, even more, the download itself is the response of the server.
---UPDATE----
I've been read this question:
Can I post JSON without using AJAX?
The only thing I don't understand from that question is how to make it work, thinking in that I've a "link" to download
I assume you do not want to refresh the whole page so there is a workaround.
1) Ajax-load an iframe which is a separate aspx file for example.
2) In the codebehind of that separate aspx file, generate the file in memory and convert it to an array of bytes.
3) Then use Response to stream the bytes to the user.
Finally I resolved the issue with this (in the right way).
I just take my json object and send it trough POST with a dynamic form generated with javfascript
var dataToPostInExport = JSON.stringify(queryToVerify);
//Convert To POST and send
var VerifyForm = document.createElement("form");
VerifyForm.target = "_blank";
VerifyForm.method = "POST";
VerifyForm.action = "file.ashx";
var dataInput = document.createElement("input");
dataInput.type = "hidden";
dataInput.name = "mydata";
dataInput.value = dataToPostInExport;
VerifyForm.appendChild(dataInput);
document.body.appendChild(VerifyForm);
VerifyForm.submit();
Then in the ashx file:
Dim DataToParse As String
DataToParse = HttpContext.Current.Request.Form("mydata")
Dim JSSerializer As New JavaScriptSerializer
Dim QueryToExport as my very own type!
QueryToExport = JSSerializer.Deserialize(Of My Own Type)(dataToParse)