我是将一个zip文件从Java桌面应用程序上传到一个Httpserver(运行Tomcat 7),我正在使用Apache httpClient 4.5.3并显示一个进度条,显示使用此包装器解决方案的进度https://github.com/x2on/gradle-hockeyapp-plugin/blob/master/src/main/groovy/de/felixschulze/gradle/util/ProgressHttpEntityWrapper.groovy
因此,在我的代码中,每次调用回调时都会更新进度条
HttpEntity reqEntity = MultipartEntityBuilder.create()
.addPart("email", comment)
.addPart("bin", binaryFile)
.build();
ProgressHttpEntityWrapper.ProgressCallback progressCallback = new ProgressHttpEntityWrapper.ProgressCallback() {
@Override
public void progress(final float progress) {
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
MainWindow.logger.severe("progress:"+progress);
Counters.getUploadSupport().set((int)progress);
SongKong.refreshProgress(CreateAndSendSupportFilesCounters.UPLOAD_SUPPORT_FILES);
}
}
);
}
};
httpPost.setEntity(new ProgressHttpEntityWrapper(reqEntity, progressCallback));
HttpResponse response = httpclient.execute(httpPost);
HttpEntity resEntity = response.getEntity();
MainWindow.logger.severe("HttpResponse:"+response.getStatusLine());
这会报告以百分比形式上传的文件,但报告100%创建和实际从服务器接收http状态之间存在相当大的延迟。
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.19408
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.40069
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.6073
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.81391
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.99768
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.99778
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.99789
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.999794
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:99.9999
07/07/2017 14.23.54:BST:CreateSupportFile$4$1:run:SEVERE: progress:100.0
07/07/2017 14.24.11:BST:CreateSupportFile:sendAsHttpPost:SEVERE: HttpResponse:HTTP/1.1 200 OK
07/07/2017 14.24.11:BST:CreateSupportFile:sendAsHttpPost:SEVERE: Unknown Request
注意不是因为我的tomcat代码做了很多,因为我还没有为这个函数实现tomcat代码所以它只是默认为“Unknown Request”代码。
protected void doPost(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException, java.io.IOException
{
String createMacUpdateLicense = request.getParameter(RequestParameter.CREATEMACUPDATELICENSE.getName());
if(createMacUpdateLicense!=null)
{
createMacUpdateLicense(response, createMacUpdateLicense);
}
else
{
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain; charset=UTF-8; charset=UTF-8");
response.getWriter().println("Unknown Request");
response.getWriter().close();
}
}
如何在用户完成时更准确地向用户报告
更新 我现在已经完全实现了服务器端,这增加了差异
@Override
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException, java.io.IOException
{
String uploadSupportFiles = request.getParameter(RequestParameter.UPLOADSUPPORTFILES.getName());
if(uploadSupportFiles!=null)
{
uploadSupportFiles(request, response, uploadSupportFiles);
}
else
{
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain; charset=UTF-8; charset=UTF-8");
response.getWriter().println("Unknown Request");
response.getWriter().close();
}
}
private void uploadSupportFiles(HttpServletRequest request, HttpServletResponse response, String email) throws IOException
{
Part filePart;
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain; charset=UTF-8; charset=UTF-8");
try
{
filePart = request.getPart("bin");
String fileName = getSubmittedFileName(filePart);
response.getWriter().println(email+":File:" + fileName);
//Okay now save the zip file somewhere and email notification
File uploads = new File("/home/jthink/songkongsupport");
File supportFile = new File(uploads, email+".zip");
int count =0;
while(supportFile.exists())
{
supportFile = new File(uploads, email+"("+count+").zip");
count++;
}
InputStream input;
input = filePart.getInputStream();
Files.copy(input, supportFile.toPath());
Email.sendAlert("SongKongSupportUploaded:" + supportFile.getName(), "SongKongSupportUploaded:" + supportFile.getName());
response.getWriter().close();
}
catch(ServletException se)
{
response.getWriter().println(email+":"+se.getMessage());
response.getWriter().close();
}
}
答案 0 :(得分:3)
假设您的服务器端代码只是将上传的文件写入某处并响应类似" DONE"最后,这是发生的事情的粗略时间表:
Bytes written to socket OutputStream
============================|
<--> Buffering |
Bytes sent by TCP stack |
============================
<------> Network latency|
Bytes received by Tomcat
============================
| (Tomcat waits for all data to finish uploading
| before handing it out as "parts" for your code)
| File written to local file on server
| =====
|
| Response "DONE" written by servlet to socket output
| ==
| <---> Network latency
| == Response "DONE" received by client
| |
| |
"100%" for entity wrapper ^ Actual 100% ^
Discrepancy
<----------------------->
"Twilight Zone" : part of discrepancy you cannot do much about.
(progress feedback impossible without using much lower level APIs)
<--------------------->
衡量标准当然是完全随意的,但它表明有几个因素可以参与这种差异。
您的服务器在收到所有字节后写入文件,但这并没有太大区别。
所以,因素:
因此,您可以将这种差异考虑在内并调整上传完成情况&#34;步进到总进度的90%,并在得到最终响应时从90跳到100。从0%到90%,用户会看到&#34;正在上传&#34;,一个很好的进度条移动,然后你显示&#34;处理...&#34;,也许有一个悸动,并在完成,跳到100%。
这是许多其他工具的功能。即使我用浏览器下载文件,也有一个小的延迟,下载似乎停留在&#34;几乎&#34;在文件实际可用之前,100%一秒钟(或更多在我的旧计算机上)。
如果&#34;暮光区&#34;时间远远高于你的进度包装器所感知的上传时间,你可能会遇到问题,因此你的问题就是&#34;这种延迟来自何处?&#34; (现在我不知道)。在这种情况下,请提供完整的时间安排(并确保客户端和服务器机器的时钟同步)。
如果 reaaaally 需要更加准确/顺利的进度报告,您需要更多参与设置。您可能需要在服务器端使用更多低级API(例如,不使用@MultipartConfig
等),以便让您的服务器执行类似于接收数据时写入磁盘的操作(这会使错误处理更多很难),打印一个点输出并刷新,每写1%的文件写入磁盘(或任何其他类型的进展,只要它在服务器上的实际进度) -侧)。然后,您的客户端将能够逐步读取该响应,并获得准确的进度报告。你可以避免在客户端进行线程化,按顺序执行此操作是可以的:
即便如此,我也不确定是否可以显示所有步骤的进度信息(特别是在100%发送和服务器可能发送的第一个字节之间),所以即使是非常复杂的设置也是如此会没用的(它可能会暂时停留在90%,然后立即转到91/92 / ...... 99/100)。
所以在这一点上它可能不值得。如果您在客户端发送的最后一个字节和收到的响应之间确实有17秒的步长,那么其他内容就会关闭。最初我假设它是用于大量文件,但从那以后你说你的文件高达50MB,所以你可能还有别的东西要看。
答案 1 :(得分:0)
某些服务器端代码可能会根据块数据的表示方式而改变,但概念大致相同。假设您正在上传一个10MB的文件,并且您的块大小设置为1MB。您将向服务器发送10个请求,每个请求包含1MB数据。客户实际上有责任打破这一切。这就是你将在Javascript中做的事情。然后,通过HttpRequest发送每个请求以及有关文件,块号和块数的一些其他数据。我再次使用plupload插件为我处理这个问题,因此一些Request数据可能会在实现之间有所不同。
我向您展示的方法是输出JSON的Webservice的一部分 数据回到客户端。然后你的javascript可以解析JSON和 寻找错误或成功消息并采取适当行动。根据 在您的实施中,您发回的数据可能会有所不同。 javascript将最终处理进度条或百分比 或者其他什么,随着它上传成功的块上传而增加它。我的 我的项目的实现让plupload处理所有这些,但是 也许我给你的那篇文章会给你更多的控制权 客户端。
protected void Upload()
{
HttpPostedFile file = Request.Files[0];
String relativeFilePath = "uploads/";
try
{
if(file == null)
throw new Exception("Invalid Request.");
//plupload uses "chunk" to indicate which chunk number is being sent
int chunk = (int)Request.Form["chunk"];
//plupload uses "chunks" to indicate how many total chunks are being sent
int chunks = (int)Request.Form["chunks"];
//plupload uses "name" to indicate the original filename for the file being uploaded
String filename = Request.Form["name"];
relativeFilePath += filename;
//Create a File Stream to manage the uploaded chunk using the original filename
//Note that if chunk == 0, we are using FileMode.Create because it is the first chunk
//otherwise, we use FileMode.Append to add to the byte array that was previously saved
using (FileStream fs = new FileStream(Server.MapPath(relativeFilePath), chunk == 0 ? FileMode.Create : FileMode.Append))
{
//create the byte array based on the data uploaded and save it to the FileStream
var buffer = new byte[file.InputStream.Length];
file.InputStream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
if((chunks == 0) || ((chunks > 0)&&(chunk == (chunks - 1))))
{
//This is final cleanup. Either there is only 1 chunk because the file size
//is less than the chunk size or there are multiple chunks and this is the final one
//At this point the file is already saved and complete, but maybe the path is only
//temporary and you want to move it to a final location
//in my code I rename the file to a GUID so that there is never a duplicate file name
//but that is based on my application's needs
Response.Write("{\"success\":\"File Upload Complete.\"}");
}
else
Response.Write("{\"success\":\"Chunk "+chunk+" of "+chunks+" uploaded.\"}");
}
catch(Exception ex)
{
//write a JSON object to the page and HtmlEncode any quotation marks/HTML tags
Response.Write("{\"error\":\""+HttpContext.Current.Server.HtmlEncode(ex.Message)+"\"});
}
}