Skip to content

Commit

Permalink
Get uploaded size while upload is in progress
Browse files Browse the repository at this point in the history
Proposal to fix issue #3636

Motivations:
Currently, while adding the next buffers to the decoder
(`decoder.offer()`), there is no way to access to the current HTTP
object being decoded since it can only be available currently once fully
decoded by `decoder.hasNext()`.
Some could want to know the progression on the overall transfer but also
per HTTP object.
While overall progression could be done using (if available) the global
Content-Length of the request and taking into account each HttpContent
size, the per HttpData object progression is unknown.

Modifications:
1) For HTTP object, `AbstractHttpData` has 2 protected properties named
`definedSize` and `size`, respectively the supposely final size and the
current (decoded until now) size.
This provides a new method `definedSize()` to get the current value for
`definedSize`. The `size` attribute is reachable by the `length()`
method.

Note however there are 2 different ways that currently managed the
`definedSize`:
a) `Attribute`: it is reset each time the value is less than actual
(when a buffer is added, the value is increased) since the final length
is not known (no Content-Length)
b) `FileUpload`: it is set at startup from the lengh provided

So these differences could lead in wrong perception;
a) `Attribute`: definedSize = size always
b) `FileUpload`: definedSize >= size always

Therefore the comment tries to explain clearly the different behaviors.

2) In the InterfaceHttpPostRequestDecoder (and the derived classes), I
add a new method: `decoder.currentPartialHttpData()` which will return a
`InterfaceHttpData` (if any) as the current `Attribute` or `FileUpload`
(the 2 generic types), which will allow then the programmer to check
according to the real type (instance of) the 2 methods `definedSize()`
and `length()`.

This method check if currentFileUpload or currentAttribute are null and
returns the one (only one could be not null) that is not null.

Note that if this method returns null, it might mean 2 situations:
a) the last `HttpData` (whatever attribute or file upload) is already
finished and therefore accessible through `next()`
b) there is not yet any `HttpData` in decoding (body not yet parsed for
instance)

Result:
The developper has more access and therefore control on the current
upload.
The coding from developper side could looks like in the example in
HttpUloadServerHandler.
  • Loading branch information
fredericBregier committed Apr 18, 2015
1 parent 392cdc1 commit 6c098ee
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 0 deletions.
Expand Up @@ -105,6 +105,11 @@ public long length() {
return size;
}

@Override
public long definedLength() {
return definedSize;
}

@Override
public ByteBuf content() {
try {
Expand Down
Expand Up @@ -98,6 +98,23 @@ public interface HttpData extends InterfaceHttpData, ByteBufHolder {
*/
long length();

/**
* Returns the current defined length of the HttpData.
*
* For {@link Attribute}, this is equivalent to the current size (length()) since the
* decoding does not known the final size yet (no Content-Length), therefore the defined length
* is always the current length (whatever during decoding or in final state).
*
* For {@link FileUpload}, this equivalent to the given defined length through Content-Length
* argument. This value does not changed, whatever during decoding or in the final state.
*
* This method could be used for instance to know the amount of current amount of bytes transmitted for
* one particular HttpData, specially one {@link FileUpload}.
*
* @return the current defined length of the HttpData
*/
long definedLength();

/**
* Deletes the underlying storage for a file item, including deleting any
* associated temporary disk file.
Expand Down
Expand Up @@ -393,6 +393,15 @@ public InterfaceHttpData next() {
return null;
}

@Override
public InterfaceHttpData currentPartialHttpData() {
if (currentFileUpload != null) {
return currentFileUpload;
} else {
return currentAttribute;
}
}

/**
* This getMethod will parse as much as possible data and fill the list and map
*
Expand Down
Expand Up @@ -238,6 +238,11 @@ public InterfaceHttpData next() {
return decoder.next();
}

@Override
public InterfaceHttpData currentPartialHttpData() {
return decoder.currentPartialHttpData();
}

@Override
public void destroy() {
decoder.destroy();
Expand Down
Expand Up @@ -350,6 +350,11 @@ public InterfaceHttpData next() {
return null;
}

@Override
public InterfaceHttpData currentPartialHttpData() {
return currentAttribute;
}

/**
* This getMethod will parse as much as possible data and fill the list and map
*
Expand Down
Expand Up @@ -121,6 +121,16 @@ public interface InterfaceHttpPostRequestDecoder {
*/
InterfaceHttpData next();

/**
* Returns the current InterfaceHttpData if currently in decoding status,
* meaning all data are not yet within, or null if there is no InterfaceHttpData
* currently in decoding status (either because none yet decoded or none currently partially
* decoded). Full decoded ones are accessible through hasNext() and next() methods.
*
* @return the current InterfaceHttpData if currently in decoding status or null if none.
*/
InterfaceHttpData currentPartialHttpData();

/**
* Destroy the {@link InterfaceHttpPostRequestDecoder} and release all it resources. After this method
* was called it is not possible to operate on it anymore.
Expand Down
Expand Up @@ -148,6 +148,11 @@ public long length() {
return attribute.length();
}

@Override
public long definedLength() {
return attribute.definedLength();
}

@Override
public boolean renameTo(File dest) throws IOException {
return attribute.renameTo(dest);
Expand Down
Expand Up @@ -151,6 +151,11 @@ public long length() {
return fileUpload.length();
}

@Override
public long definedLength() {
return fileUpload.definedLength();
}

@Override
public boolean renameTo(File dest) throws IOException {
return fileUpload.renameTo(dest);
Expand Down
Expand Up @@ -70,6 +70,8 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj

private boolean readingChunks;

private FileUpload partialContent;

private final StringBuilder responseContent = new StringBuilder();

private static final HttpDataFactory factory =
Expand Down Expand Up @@ -216,6 +218,11 @@ private void readHttpDataChunkByChunk() {
while (decoder.hasNext()) {
InterfaceHttpData data = decoder.next();
if (data != null) {
// check if current HttpData is a FileUpload and previously set as partial
if (partialContent == data) {
System.out.println(".100% (" + partialContent.length() + ")");
partialContent = null;
}
try {
// new value
writeHttpData(data);
Expand All @@ -224,6 +231,18 @@ private void readHttpDataChunkByChunk() {
}
}
}
// Check partial decoding for a FileUpload
InterfaceHttpData data = decoder.currentPartialHttpData();
if (data != null && data instanceof FileUpload) {
if (partialContent != null && partialContent != data) {
// end of previous partial content
System.out.println(".100% (" + partialContent.length() + ")");
} else if (partialContent == null) {
partialContent = (FileUpload) data;
System.out.print("Start FileUpload: " + partialContent.getFilename() + " ");
}
System.out.print((partialContent.length() * 100 / partialContent.definedLength()) + "%.");
}
} catch (EndOfDataDecoderException e1) {
// end
responseContent.append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n");
Expand Down

0 comments on commit 6c098ee

Please sign in to comment.