0

我正在尝试实现 JSF 2.3 文件上传:

<context-param>
    <param-name>javax.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name>
    <param-value>true</param-value>
</context-param>

XHTML 页面:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>About</title>
        <script type="text/javascript">
            function progressBar(data) {
                if (data.status == "begin") {
                    document.getElementById("progressBarId").style.display = "block";
                }
                else if (data.status == "complete") {
                    document.getElementById("progressBarId").style.display = "none";
                }
            }

            function updateProgressBar(percent) {
                var emptyColor = "#cccccc";
                var progressColor = "#3333cc";
                document.getElementById("progressBarId").style.background = "linear-gradient(to right, " + progressColor + " 0%, " + progressColor + " " + percent + "%, " + emptyColor + " " + percent + "%, " + emptyColor + " 100%)";
            }
        </script>
    </h:head>
    <h:body>
        <f:websocket channel="uploadProgress" scope="view" onmessage="updateProgressBar" />

        <h:messages id="uploadMsgId" globalOnly="true" showDetail="false" showSummary="true" style="color:red"/>
        <h:form id="uploadFormId" enctype="multipart/form-data">
            <h:inputFile id="fileToUpload" required="true" requiredMessage="No file selected ..." value="#{uploadBean.file}"/>
            <h:message showDetail="false" showSummary="true" for="fileToUpload" style="color:red"/>
            <h:commandButton value="Upload" action="#{uploadBean.upload()}">
                <f:ajax execute="fileToUpload" onevent="progressBar" render=":uploadMsgId @form"/>
            </h:commandButton>
        </h:form>
        <div>
            <div id="progressBarId" style="display: none; width: 250px; height: 23px; border: 1px solid black;" width="250px;" height="23"/>
        </div>

    </h:body>
</html>

豆:

@Named
@RequestScoped
public class UploadBean
{
    @Inject
    @Push
    private PushContext uploadProgress;

    private static final Logger logger = Logger.getLogger(UploadBean.class.getName());
    private Part file;

    public Part getFile()
    {
        return file;
    }

    public void setFile(Part file)
    {
        this.file = file;
    }

    public void upload()
    {

        if (file != null)
        {

            logger.info("File Details:");
            logger.log(Level.INFO, "File name:{0}", file.getName());
            logger.log(Level.INFO, "Content type:{0}", file.getContentType());
            logger.log(Level.INFO, "Submitted file name:{0}", file.getSubmittedFileName());
            logger.log(Level.INFO, "File size:{0}", file.getSize());

            try (InputStream inputStream = file.getInputStream(); FileOutputStream outputStream = new FileOutputStream("C:" + File.separator + "jsf_files_test_for_delete" + File.separator + file.getSubmittedFileName()))
            {

                long lastTimestamp = System.currentTimeMillis();
                int pushInterval = 1000;
                long totalRead = 0;
                long size = file.getSize();

                int bytesRead = 0;
                final byte[] chunck = new byte[1024];
                while ((bytesRead = inputStream.read(chunck)) != -1)
                {
                    outputStream.write(chunck, 0, bytesRead);
                    totalRead += bytesRead;

                    if (System.currentTimeMillis() > lastTimestamp + pushInterval)
                    {
                        lastTimestamp = System.currentTimeMillis();
                        uploadProgress.send(100.0 * totalRead / size); // Make sure this isn't sent more often than once per second.
                    }
                }

                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Upload successfully ended!"));
            }
            catch (IOException e)
            {
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Upload failed!"));
            }
        }
    }
}

按下上传后,我等待一段时间等待上传过程。看起来代码有问题,我找不到。你能告诉我如何解决这个问题吗?

4

1 回答 1

0

文件上传工作如下:

  1. 客户端向服务器发送文件。
  2. 服务器检索文件并保存在内存或临时磁盘中。
  3. JSF bean 方法将文件保存/移动到永久位置。

此代码仅计算第 3 步的进度,而不是第 1 步和第 2 步。<h:inputFile>不幸的是,无法计算前两个步骤的进度,因为:

  • <h:inputFile>不使用HTML5+XHR 文件上传,而是一个iframe 技巧,用于向后兼容旧版浏览器。无法在 JavaScript 中挂钩 iframe 提交进度。
  • 所使用的Servlet 3.0 PartAPI<h:inputFile>不使用流式传输,而是在 JSF 支持 bean 方法被命中之前先完全保存在内存或临时磁盘中。无法使用本机 Servlet API 来挂钩服务器端上传请求解析进度。

因此,在使用 JSF 方法之前通过网络将文件从浏览器发送到服务器的进度仍然未知。也就是说,当您依赖标准 API 时。理论上的解决方案是创建一个使用 HTML5+XHR 文件上传 API 的自定义组件。

在 JSF 后备 bean 方法中保存上传文件的进度是可能的,例如基于 websocket 的推送,但是与将文件发送到服务器相比,在服务器端保存文件是如此之快,以至于只显示这部分进度是毫无意义的.

不幸的是,它就是这样。考虑使用<p:fileUpload>.

于 2016-04-25T06:29:11.113 回答