Hi, > I wouldn't say that. File transfers are affected by IO, not CPU. I've read this somewhere several months ago and yesterday again here: https://blogs.warwick.ac.uk/chrismay/entry/mod_x_sendfile/
> I'd like to see how you implemented it. Basically like the HowTo [https://wiki.apache.org/tapestry/Tapestry5HowToStreamAnExistingBinaryFile] but I shortened it a little bit: AttachmentStreamResponse.java: public class AttachmentStreamResponse implements StreamResponse { private InputStream inputStream = null; private String contentType = "text/plain"; private FileReference fileReference; public AttachmentStreamResponse(FileReference fileReference) throws FileNotFoundException { this.fileReference = fileReference; this.inputStream = new BufferedInputStream(new FileInputStream(fileReference.getAbsolutePath())); } [getter for contentType and inputStream] @Override public void prepareResponse(Response response) { response.setHeader("Content-Disposition", "attachment; filename=\"" + fileReference.getFileName() + "\""); response.setContentLength((int) fileReference.getSizeInBytes()); response.setHeader("Expires", "0"); response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); response.setHeader("Pragma", "public"); } } And in the Component, where the Actionlink points to: private StreamResponse onActionFromDownload(long fileReferenceId, String folderName) throws IOException { FileReference fileToSend = new FileReference(fileReferenceId); if (folderName != null && [... is file existing and allowed to download? ...]) { return new AttachmentStreamResponse(fileToSend); } return null; } The only ugly thing for users who want to copy the direct file link was the event request: [server]/index.ajaxuploadarea.download/61/nEWEaMExZ5xbJzSmNQQe?t:ac=5tAnW, where 61 is the fileReferenceId and nEW.. the folderName. I've now also integrated url rewriting (as http://blog.tapestry5.de/index.php/2010/09/06/new-url-rewriting-api/). This took the most time :-D public class FileDownloadLinkTransformer implements ComponentEventLinkTransformer { private enum ManuallyTransformedRequest { FILE_DOWNLOAD, NOT_MANUALLY_TRANSFORMED; } private final String FILE_DOWNLOAD_COMPONENT_PATH = "/" + Index.class.getSimpleName().toLowerCase() + ".ajaxuploadarea.download"; private final String FILE_DOWNLOAD_COMPONENT_PATH_REGEX = "/" + Index.class.getSimpleName().toLowerCase() + "\\.ajaxuploadarea\\.download.*"; private final String FILE_DOWNLOAD_COMPONENT_PATH_TRANSFORMATION = "/download"; private final String ACTIVATION_CONTEXT_PARAMETER_NAME = "t:ac"; @Inject private TypeCoercer typeCoercer; @Override public Link transformComponentEventLink(Link defaultLink, ComponentEventRequestParameters parameters) { switch (getTypeOfPage(defaultLink)) { case FILE_DOWNLOAD: String context = exctractFileIdAndFolderName(defaultLink); if (context != null) { defaultLink.removeParameter(ACTIVATION_CONTEXT_PARAMETER_NAME); return defaultLink.copyWithBasePath(getDevelopeModeExtension() + FILE_DOWNLOAD_COMPONENT_PATH_TRANSFORMATION + context); } break; case NOT_MANUALLY_TRANSFORMED: break; } return defaultLink; } @Override public ComponentEventRequestParameters decodeComponentEventRequest(Request request) { String requestedPath = request.getPath(); if (requestedPath.startsWith(FILE_DOWNLOAD_COMPONENT_PATH_TRANSFORMATION)) { return new ComponentEventRequestParameters( Index.class.getSimpleName(), Index.class.getSimpleName(), FILE_DOWNLOAD_COMPONENT_PATH.replaceFirst("/" + Index.class.getSimpleName().toLowerCase() + "\\.", ""), EventConstants.ACTION, new EmptyEventContext(), toEventContext(requestedPath.substring(FILE_DOWNLOAD_COMPONENT_PATH_TRANSFORMATION.length() + 1))); } return null; } private ManuallyTransformedRequest getTypeOfPage(Link link) { if (link.getBasePath().matches(getDevelopeModeExtension() + FILE_DOWNLOAD_COMPONENT_PATH_REGEX)) { return ManuallyTransformedRequest.FILE_DOWNLOAD; } return ManuallyTransformedRequest.NOT_MANUALLY_TRANSFORMED; } private String exctractFileIdAndFolderName(Link link) { if (link.getBasePath().matches(getDevelopeModeExtension() + FILE_DOWNLOAD_COMPONENT_PATH + "/[0-9]+/[0-9a-zA-Z].+")) { return link.getBasePath().substring((getDevelopeModeExtension() + FILE_DOWNLOAD_COMPONENT_PATH).length()); } return null; } private EventContext toEventContext(String value) { return value == null ? new EmptyEventContext() : new ArrayEventContext(typeCoercer, (Object[]) value.split("/")); } private String getDevelopeModeExtension() { return PlainTraySetting.PRODUCTION_MODE ? "" : "/webapp"; } } I know, it got a bit ugly, but at least it works and the most parts are strongly typed. René On 24.01.2013 15:29, Thiago H de Paula Figueiredo wrote: > On Thu, 24 Jan 2013 11:49:05 -0200, René Bernhardsgrütter > <rene.bernhardsgruet...@gmail.com> wrote: > >> Hi Thiago, > > Hi! > >> thank you for your answer! > > ;) > >> I thought that Java/Tomcat wouldn't be optimal to handle large/many file >> transfers in a web environment (too high cpu usage) and that Apache >> would handle it better -- correct me, if I'm wrong. > > I wouldn't say that. File transfers are affected by IO, not CPU. > >> I've now implemented the StreamResponse solution you suggested and it >> works fine. > > I'd like to see how you implemented it. > >> Anyway, I'll see if the performance will be a problem. > > That's exactly how you should do: instead of just guessing how it'll > perform, implement and test. :) > --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org