iseeall
iseeall

Reputation: 3431

0-byte files not detected when downloading files on Android

I have an app for Android which downloads hundreds of files from the Internet. Some files turn out to be 0-byte after download. The app attempts to detect such cases and delete such files after download but sometimes it fails. The problem is more frequent on Android 4.x devices.

Here is the method which does the downloading. I gets the number of actually read bytes from inputStream.read(buffer).

public class Utils
{
public static class DownloadFileData
{
    int nTotalSize;
    int nDownloadedSize;
}
public interface ProgressCallback
{
    void onProgress(long nCurrent, long nMax);
}
public static boolean downloadFile(String sFileURL, File whereToSave, DownloadFileData fileData, ProgressCallback progressCallback)
{
    InputStream inputStream = null;
    FileOutputStream fileOutput = null;
    try
    {
        URL url = new URL(sFileURL);
        URLConnection connection = url.openConnection();

    //set up some things on the connection
        connection.setDoOutput(true);
        connection.connect();

        fileOutput = new FileOutputStream(whereToSave);
        inputStream = connection.getInputStream();
        fileData.nTotalSize = connection.getContentLength();
        fileData.nDownloadedSize = 0;

            byte[] buffer = new byte[1024];
            int bufferLength = 0; //used to store a temporary size of the buffer

        // now, read through the input buffer and write the contents to the file
        while ((bufferLength = inputStream.read(buffer)) > 0)
        {
            // if interrupted, don't download the file further and return
            // also restore the interrupted flag so that the caller stopped also
            if (Thread.interrupted())
            {
                Thread.currentThread().interrupt();
                return false;
            }

            // add the data in the buffer to the file in the file output stream
            fileOutput.write(buffer, 0, bufferLength);
            // add up the size so we know how much is downloaded
            fileData.nDownloadedSize += bufferLength;

            if (null != progressCallback && fileData.nTotalSize > 0)
            {
                progressCallback.onProgress(fileData.nDownloadedSize, fileData.nTotalSize);
            }
        }
  return true;
    }
    catch (FileNotFoundException e)
    {
        return false; // swallow a 404
    }
    catch (IOException e)
    {
        return false; // swallow a 404
    }
    catch (Throwable e)
    {
        return false;
    }
    finally
    {
        // in any case close input and output streams
        if (null != inputStream)
        {
            try
            {
                inputStream.close();
                inputStream = null;
            }
            catch (Exception e)
            {
            }
        }
        if (null != fileOutput)
        {
            try
            {
                fileOutput.close();
                fileOutput = null;
            }
            catch (Exception e)
            {
            }
        }
    }
}

Here is the piece of code which processes the downloads. Since sometimes the number of read bytes is incorrect (it is > 0 and the real file has the size 0 bytes) I check the size of the downloaded file with outputFile.length(). But this again gives a value > 0 even though the file is really 0 byte. I tried to also just create a new file and read its size with recheckSizeFile.length(). Still the size is determined as > 0 while it's really 0 byte.

Utils.DownloadFileData fileData = new Utils.DownloadFileData();
boolean bDownloadedSuccessully = Utils.downloadFile(app.sCurrenltyDownloadedFile, outputFile, fileData, new Utils.ProgressCallback()
                    {
... // progress bar is updated here
});

if (bDownloadedSuccessully)
{
    boolean bIsGarbage = false;
    File recheckSizeFile = new File(sFullPath);
    long nDownloadedFileSize = Math.min(recheckSizeFile.length(), Math.min(outputFile.length(), fileData.nDownloadedSize));
        // if the file is 0bytes, it's garbage
    if (0 == nDownloadedFileSize)
    {
        bIsGarbage = true;
    }
    // if this is a video and if of suspiciously small size, it's
    // garbage, too
    else if (Utils.isStringEndingWith(app.sCurrenltyDownloadedFile, App.VIDEO_FILE_EXTENSIONS) && nDownloadedFileSize < Constants.MIN_NON_GARBAGE_VIDEO_FILE_SIZE)
    {
        bIsGarbage = true;
    }
    if (bIsGarbage)
    {
        ++app.nFilesGarbage;
        app.updateLastMessageInDownloadLog("File is fake, deleting: " + app.sCurrenltyDownloadedFile);
        // delete the garbage file
        if (null != outputFile)
        {
            if (!outputFile.delete())
            {
                Log.e("MyService", "Failed to delete garbage file " + app.sCurrenltyDownloadedFile);
            }
        }
    }
    else
    {
        ... // process the normally downloaded file
    }

I am not sure but I think there is a bug in Android with reading file size. Has anyone seen a similar problem? Or am I maybe doing something wrong here? Thanks!

EDIT: how i determine that the files are 0-byte: all the files which get downloaded go thru the described routines. When I then later view the download folder with a file browser (Ghost Commander), some of the files (like maybe 10%) are 0-byte. They can't be played by a video player (shown as "broken file" icon).

Upvotes: 4

Views: 3650

Answers (2)

James Holderness
James Holderness

Reputation: 23001

It looks to me like your problem is that you only check for "garbage" files if the Utils.downloadFile call returns true. If the download fails in the getInputStream call or the first read, you will have created a file with zero length which will never be deleted.

Upvotes: 1

user908627
user908627

Reputation:

You should call flush() on your FileOutputStream to ensure that all data is written to the file. This should make your issue with 0-byte files occur less often.

To check for 0 byte files using File.length() should work properly. Can you open a shell (adb shell) on the device and run ls -l to see the byte count displayed by it is 0 (maybe your file manager has some weird issues). Also please debug (or put some log statements) that sFullPath contains the correct file paths. I can't see where sFullPath gets set in your code above and why you don't just use outputFile but recreate another File object.

Upvotes: 1

Related Questions