0

i use JSF and want to have file download in my page . i wrote some codes but i get ClientAbortException error when i use some download manager for download my file :

public class FileUtil  {


public static FacesContext getContext() {
    return FacesContext.getCurrentInstance();
}

public static void sendFile(File file, boolean attachment) throws IOException {
    sendFile(getContext(), file, attachment);
}

public static void sendFile(FacesContext context, File file, boolean attachment) throws IOException {
    sendFile(context, new FileInputStream(file), file.getName(), file.length(), attachment);
}

public static void sendFile(FacesContext context, byte[] content, String filename, boolean attachment) throws IOException {
    sendFile(context, new ByteArrayInputStream(content), filename, (long) content.length, attachment);
}

public static void sendFile(FacesContext context, InputStream content, String filename, boolean attachment) throws IOException {
    sendFile(context, content, filename, -1L, attachment);
}

private static void sendFile(FacesContext context, InputStream input, String filename, long contentLength, boolean attachment) throws IOException {
    ExternalContext externalContext = context.getExternalContext();
    externalContext.setResponseBufferSize(10240);
    externalContext.setResponseContentType(getMimeType(context, filename));
    externalContext.setResponseHeader("Content-Disposition", String.format("%s;filename=\"%2$s\"; filename*=UTF-8\'\'%2$s", new Object[]{attachment ? "attachment" : "inline", encodeURL(filename)}));
    if (((HttpServletRequest) externalContext.getRequest()).isSecure()) {
        externalContext.setResponseHeader("Cache-Control", "public");
        externalContext.setResponseHeader("Pragma", "public");
    }

    if (contentLength != -1L) {
        externalContext.setResponseHeader("Content-Length", String.valueOf(contentLength));
    }

    long size = stream(input, externalContext.getResponseOutputStream());
    if (contentLength == -1L) {
        externalContext.setResponseHeader("Content-Length", String.valueOf(size));
    }

    context.responseComplete();
}


public static String getMimeType(FacesContext context, String name) {
    String mimeType = context.getExternalContext().getMimeType(name);
    if (mimeType == null) {
        mimeType = "application/octet-stream";
    }

    return mimeType;
}

public static long stream(InputStream input, OutputStream output) throws IOException {



    ReadableByteChannel inputChannel = Channels.newChannel(input);
    Throwable var3 = null;

    try {
        WritableByteChannel outputChannel = Channels.newChannel(output);
        Throwable var5 = null;

        try {
            ByteBuffer buffer = ByteBuffer.allocateDirect(10240);
            long size = 0L;

            while (inputChannel.read(buffer) != -1) {
                buffer.flip();
                size += (long) outputChannel.write(buffer);
                buffer.clear();
            }

            long var9 = size;
            return var9;
        } catch (Throwable var33) {
            var5 = var33;
            throw var33;
        } finally {
            if (outputChannel != null) {
                if (var5 != null) {
                    try {
                        outputChannel.close();
                    } catch (Throwable var32) {
                        var5.addSuppressed(var32);
                    }
                } else {
                    outputChannel.close();
                }
            }

        }
    } catch (Throwable var35) {
        var3 = var35;
        throw var35;
    } finally {
        if (inputChannel != null) {
            if (var3 != null) {
                try {
                    inputChannel.close();
                } catch (Throwable var31) {
                    var3.addSuppressed(var31);
                }
            } else {
                inputChannel.close();
            }
        }

    }

}

public static String encodeURL(String string) {
    if (string == null) {
        return null;
    } else {
        try {
            return URLEncoder.encode(string, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException var2) {
            throw new UnsupportedOperationException("UTF-8 is apparently not supported on this platform.", var2);
        }
    }
}

}

something that i can not understand is when download is done by native chorome download without usage of any download manager like IDM or eagleget , I Do not get any ClientAbortException , but when i use these download manager software for (enable their AddOns) i get these error

what happens ? i know this error happens with some connection losing ... but i did not close my page or any thing that cause this error! and this is my bean code:

@ManagedBean(name = "bean")
@RequestScoped
public class MB implements Serializable {

public void MBdowan() throws IOException {


    File file = new File("E:\\Animation\\IA\\Learning movies\\webinar1\\01_Aug_webinar_08\\Aug08_edited_webinar_animation.mov");

    FileUtil.sendFile(file,true);

}

and this is my xhtml page :

</h:head>
<h:body>
    <h:form>
        <p:commandButton value="Download file" ajax="false" actionListener="#{bean.MBdowan}"/>
    </h:form>
</h:body>

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
pooya
  • 1
  • 3
  • Download managers usually expect idempotently available files (i.e. files which are available by GET and not only by POST), most likely preferably also with HEAD support. Hard to answer without knowing the exact manager and the actual HTTP traffic. Generically, your best bet is a file servlet like this http://snapshot.omnifaces.org/servlets/FileServlet – BalusC Oct 16 '15 at 12:34
  • thanks BalusC, i use EagleGet and Download Accelerator Plus , and what do you mean exactly by post or get .. i am using JSF externalContext.getResponseOutputStream() !!! – pooya Oct 16 '15 at 12:43
  • No need to throw with exclamation marks. It are just HTTP verbs. Perhaps you'd better take a pause on JSF and learn basic HTTP first. Nonetheless, I posted an answer. – BalusC Oct 16 '15 at 12:53
  • ok sure ... thanks man .. can you give some reference for getting good understanding of HTTP – pooya Oct 16 '15 at 13:04

1 Answers1

2

Download accelerators (and media players!) expect files which are idempotently available via GET and HEAD requests (i.e. when just typing URL in browser's address bar) and preferably also support HTTP Range requests (so multiple HTTP connections could be opened to download parts simultaneously). The JSF backing bean method is only invoked on a POST request (i.e. when submitting a HTML form with method="post"). The ClientAbortException happens because the download accelerator didn't got the response it expected while sniffing for HEAD and Range support and aborted it.

If those files are static and thus not dynamic, then your best bet is to create a separate servlet which supports HEAD and preferably also HTTP Range requests.

Given that you clearly ripped off the source code from OmniFaces Faces#sendFile(), I'd suggest to rip off the source code of another OmniFaces artifact, the FileServlet. You can find snapshot showcase and source code link here: OmniFaces (2.2) FileServlet.

Here's how you could use it:

@WebServlet("/webinar_animation.mov")
public class YourFileServlet extends FileServlet {

    @Override
    protected File getFile(HttpServletRequest request) throws IllegalArgumentException {
        return new File("E:\\Animation\\IA\\Learning movies\\webinar1\\01_Aug_webinar_08\\Aug08_edited_webinar_animation.mov");
    }

}
<a href="#{request.contextPath}/webinar_animation.mov">Download file</a>

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • thanks. yes i searched a lot and finally i realized that i have to use these code from OmniFaces becuase i couldent get proper extension of file when downloading .. so you mean that instead of using File from java I/O , i have to use these class to send if to my sendFile function? – pooya Oct 16 '15 at 13:01
  • Basically, instead of a JSF form (POST) and backing bean not supporting HTTP `Range`, use a HTML link (GET) and a servlet supporting HTTP `Range`. – BalusC Oct 16 '15 at 13:07