cancel
Showing results for 
Search instead for 
Did you mean: 

Download content by uuid with streaming

spilby
Confirmed Champ
Confirmed Champ
I want to download content from alfresco by the uuid of the node. This content may be a big file, bigger than 500MB.

What's the best way to download large contents with streaming? Using the DownloadContentServlet? Or implementing a java backed webscript doing something like this:


ContentReader contentReader = contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT);
InputStream is = contentReader.getContentInputStream();
BufferedReader br= new BufferedReader(new InputStreamReader (is));


Or is more efficient another way? What's your opinion? May be a lot of users downloading contents, is the reason I want something with streaming, to not crash down the server with simultaneous downloads.

And if I use the DownloadContentServlet, how can I call it? This is the url I found to use it:


/alfresco/download/<direct|attach>/<workspace>/<store>/<nodeId>/<filename>


I must do a HttpClient call on a servlet to this url to download it? Is the same url than if I do this?


Content[] content = WebServiceFactory.getContentService().read(new Predicate(new Reference[]{fileRef}, STORE, null) , "{http://www.alfresco.org/model/content/1.0}content" );
String url = content[0].getUrl();


Or what's the difference? I don't understand these different ways…

Thank you very much!
12 REPLIES 12

kaynezhang
World-Class Innovator
World-Class Innovator
I suggest you to use DownloadContentServlet,it allow large files to be streamed directly to the browser response stream.

spilby
Confirmed Champ
Confirmed Champ
Oks, thanks for the suggest.

I need authentication. I give to users an url to download the documents with the uuid. My first idea is give the direct servlet url, (/alfresco/download/direct/workspace/store/nodeId/filename) but because I need authentication, I supose this is not possible. Is it?

My second idea is implement a java backed webscript to authenticate with user and password first. And the users can call to my webscript to download the document. But the second problem is… User don't know the file name. Only the uuid of the content node. I supose I need to obtain the file name with this uuid first.

My question is… how can I call this DownloadContentServlet inside the java webscript? With a httpclient get call inside my webscript? And how returns the response? Because a webscript have a WebScriptResponse. I put the response of the Servlet inside the webscript response? It still works with streaming?

Thanks again

kaynezhang
World-Class Innovator
World-Class Innovator
If you want to download content in share ,do it like this
1.Generate download url(I'll explain download url format later) ,
2.Append your url to share proxy servlet.
If you want to download content in your own application ,I recommend you do it like this:
1.Generate a download url using uuid,and when user click the url,the download request is sumbmitted to your proxy servlet .
2.In your proxy servlet ,get the uuid parameter and get user ticket from session(you should save alfresco ticket in session when user logged in),then send request to alfresco download url and download the content.
3.Send the content stream to user browser.

The download url to  DownloadContentServlet should be like this
/alfresco/download/attach/workspace/SpacesStore/uuid/filename?ticket=userticket 

In my case, the users knows the url to download the content. Users are programmers than obtain with their applications this url with a java webscript response that I have implemented, when they upload a file. The response of this upload returns for example the uuid and the filename.

I try to download manually a file with the url:


http://<Alfresco_IP>/alfresco/download/attach/workspace/SpacesStore/def199df-03e3-4b8e-993a-6fe1aae02733/test.pdf


This should be the ideal url that they can use to download, the problem is the ticket. They know the user and password of alfresco, but not the ticket, because they not logged in.

Solution 1. Are any url with "user" and "password" params instead of "ticket" param? Is the ideal.

Solution 2. Not use the url directly. I thank on a webscript to log in, obtain the ticket and call this url, but you recommend me a servlet better? For curiosity, why servlet are better option than a webscript? Is for the streaming with big files? I know webscripts, but I don't find examples with servlets. I don't know how it works. Where I put this servlet on alfresco and declare it to be open and users can call it?

Thanks again!

kaynezhang
World-Class Innovator
World-Class Innovator
I recommend DownloadContentServlet becuase it has the ability to stream large file directly to the response stream.
I'm afraid this url
 http://<Alfresco_IP>/alfresco/download/attach/workspace/SpacesStore/def199df-03e3-4b8e-993a-6fe1aae02733/test.pdf
do not accept username and password parameter.
If your custom webscript has streaming ability,you can just use it.I think it has same effect as DownloadContentServlet .

Uf, maybe this is more complicated that I thank at first…

The main problem is… If I call by HttpClient in my custom webscript, how can I return the response? I can do this:


GetMethod method = new GetMethod(alfrescoServletDownloadUrlWithTicket);
method.setQueryString(ticketParameter);
HttpClient client = new HttpClient(); 
int statusCode = client.executeMethod(method);   


The webscript have a WebScriptResponse parameter on the execute, but how can I return the response of the executeMethod? People that call my webscript want the content in streaming on the response…

I supose that is more simple and efficient do this work on a custom servlet, not on a webscript. But I don't know how can I implement a servlet on alfresco, i can't find examples or tutorials to make a servlet on alfresco.

Please, you know where can I find an example to do this servlet? What I need? My custom servlet only will do: login, get the ticket and call to AlfrescoDownloadServlet to return the response in streaming.

spilby
Confirmed Champ
Confirmed Champ
On question more… I read the DownloadContentServlet and BaseDownloadContentServlet code and I can see this:


String range = req.getHeader(HEADER_CONTENT_RANGE);
(…)
HttpRangeProcessor rangeProcessor = new HttpRangeProcessor(contentService);
processedRange = rangeProcessor.processRange(res, reader, range.substring(6), nodeRef, propertyQName, mimetype, req.getHeader(HEADER_USER_AGENT));
(…)
if (processedRange == false) {
// return the complete entity range
long size = reader.getSize();
res.setHeader(HEADER_CONTENT_RANGE, "bytes 0-" + Long.toString(size-1L) + "/"+Long.toString(size));
res.setHeader(HEADER_CONTENT_LENGTH, Long.toString(size));
reader.getContent( res.getOutputStream() );
}


I understand seeing this that if I want to download with streaming, I need to set the content-range on the header or the servlet give me all the content without streaming. It's all right? I need to specify the content-range when I call by HttpClient to the servlet to get the content with streaming?

kaynezhang
World-Class Innovator
World-Class Innovator
Yes,you can refer to  org.alfresco.web.app.servlet.DownloadContentServlet class and it's parent class.

kaynezhang
World-Class Innovator
World-Class Innovator

Yes,both server side and client side need to support byte ranges,you can refer to DownloadContentServlet on how to implemnt byte ranges on server side.
for client side,you should first test it whether server support byte ranges:
1.If server dose not support byte ranges ,use common way.
2.If server supports byte ranges ,add the "Range" header and Connection header field to  GET request and execute client request multi times

//send first reqeust get file length
      HttpClient httpClient = new HttpClient();
      HeadMethod httpHead = new HeadMethod(url);
      int  statusCode = httpClient.executeMethod(httpHead);
      Header[] headers = httpHead.getResponseHeaders();
      long contentLength = 0;
      for( Header header:headers){
         header.getName().equalsIgnoreCase("Content-Length");
         contentLength = Long.valueOf(header.getValue());
      }

then send multi request to download byte range stream

      GetMethod httpGet = new GetMethod(url);
      long perRequestLength = 4096;
      long startPosition = 0;
      for (startPosition = 0; startPosition < contentLength; startPosition += perRequestLength) {
         long endPosition = Math.min(4096, (int) contentLength - startPosition);
         httpGet.addRequestHeader("Connection", "Keep-Alive");
         httpGet.addRequestHeader("Range", "bytes=" + startPosition + "-"
               + endPosition);

         // Execute
         statusCode= httpClient.executeMethod(httpGet);
         InputStream inputStream = httpGet.getResponseBodyAsStream();

         // create ramdom acces file
         
         RandomAccessFile outputStream = new RandomAccessFile(file, "rw");
         outputStream.seek(startPosition);
         int count = 0;
         byte[] buffer = new byte[1024];
         while ((count = inputStream.read(buffer, 0, buffer.length)) > 0) {
            outputStream.write(buffer, 0, count);
         }
         outputStream.close();
      }