(澳门博彩导航网址) android 下载大文件开源包(多线程 断点续传)
澳门赌场开户
澳门赌场开户
当前位置 : 澳门赌场开户 > 澳门博彩导航网址

(澳门博彩导航网址) android 下载大文件开源包(多线程 断点续传)

android下载大文件是个大头!
以前做项目只支持多线程,每次下载一半,要重新下载,希望大神能够指点指点?
希望大神 推荐一些专门下载大文件的开源项目,或者推荐一下平常用什么框架下载?
希望是支持多线程,断点续传。

端点续传其实是一个很简单的原理。需要服务器端支持才行。客户端实现很简单,就算基本的读写文件。

GitHub: https://github.com/t11e/balusc_file_servlet

HTTP头示例:

  # The first 500 bytes:
  Content-Range: bytes 0-499/1234

  # The second 500 bytes:
  Content-Range: bytes 500-999/1234

  # All except for the first 500 bytes:
  Content-Range: bytes 500-1233/1234

  # The last 500 bytes:
  Content-Range: bytes 734-1233/1234

下载的时候查看HTTP头,如果HTTP头里面包含了Range等就提取出来,计算出下一块读取的位置和长度,然后用Content-Range头返回。

客户端直接读取和保存文件

读取的时候先跳过已经下载的长度。

if !downloadPath.exists 
                    downloadPath.mkdirs; 

                if outputFileCache.exists
                {
                    downloadedSize = outputFileCache.length;
                    connection.setAllowUserInteractiontrue;
                    connection.setRequestProperty"Range", "bytes=" + downloadedSize + "-";
                    connection.setConnectTimeout14000;
                    connection.connect;
                    input = new BufferedInputStreamconnection.getInputStream;
                    output = new FileOutputStreamoutputFileCache, true;
                    input.skipdownloadedSize; //Skip downloaded size
                }
                else
                {
                    connection.setConnectTimeout14000;
                    connection.connect;
                    input = new BufferedInputStreamurl.openStream;
                    output = new FileOutputStreamoutputFileCache;
                }

                fileLength = connection.getContentLength;                 


                byte data[] = new byte[1024];
                int count = 0;
                int __progress = 0;
                long total = downloadedSize;

                while count = input.readdata != -1 && !this.isInterrupted 
                {
                    total += count;
                    output.writedata, 0, count;
                    __progress = int total * 100 / fileLength;

                }
                output.flush;
                output.close;
                input.close;

服务器端使用Servlet支持端点续传下载:

/*
 * net/balusc/webapp/FileServlet.java
 *
 * Copyright C 2009 BalusC
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation, either version 3
 * of the License, or at your option any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with this library.
 * If not, see <http://www.gnu.org/licenses/>.
 */

package net.balusc.webapp;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * A file servlet supporting resume of downloads and client-side caching and GZIP of text content.
 * This servlet can also be used for images, client-side caching would become more efficient.
 * This servlet can also be used for text files, GZIP would decrease network bandwidth.
 *
 * @author BalusC
 * @link http://balusc.blogspot.com/2009/02/fileservlet-supporting-resume-and.html
 */
public class FileServlet extends HttpServlet {

    // Constants ----------------------------------------------------------------------------------

    private static final int DEFAULT_BUFFER_SIZE = 10240; // ..bytes = 10KB.
    private static final long DEFAULT_EXPIRE_TIME = 604800000L; // ..ms = 1 week.
    private static final String MULTIPART_BOUNDARY = "MULTIPART_BYTERANGES";

    // Properties ---------------------------------------------------------------------------------

    private String basePath;

    // Actions ------------------------------------------------------------------------------------

    /**
     * Initialize the servlet.
     * @see HttpServlet#init.
     */
    public void init throws ServletException {

        // Get base path path to get all resources from as init parameter.
        this.basePath = getServletContext.getRealPathgetInitParameter"basePath";

        // Validate base path.
        if this.basePath == null {
            throw new ServletException"FileServlet init param basePath is required.";
        } else {
            File path = new Filethis.basePath;
            if !path.exists {
                throw new ServletException"FileServlet init param basePath value "
                    + this.basePath + " does actually not exist in file system.";
            } else if !path.isDirectory {
                throw new ServletException"FileServlet init param basePath value "
                    + this.basePath + " is actually not a directory in file system.";
            } else if !path.canRead {
                throw new ServletException"FileServlet init param basePath value "
                    + this.basePath + " is actually not readable in file system.";
            }
        }
    }

    /**
     * Process HEAD request. This returns the same headers as GET request, but without content.
     * @see HttpServlet#doHeadHttpServletRequest, HttpServletResponse.
     */
    protected void doHeadHttpServletRequest request, HttpServletResponse response
        throws ServletException, IOException
    {
        // Process request without content.
        processRequestrequest, response, false;
    }

    /**
     * Process GET request.
     * @see HttpServlet#doGetHttpServletRequest, HttpServletResponse.
     */
    protected void doGetHttpServletRequest request, HttpServletResponse response
        throws ServletException, IOException
    {
        // Process request with content.
        processRequestrequest, response, true;
    }

    /**
     * Process the actual request.
     * @param request The request to be processed.
     * @param response The response to be created.
     * @param content Whether the request body should be written GET or not HEAD.
     * @throws IOException If something fails at I/O level.
     */
    private void processRequest
        HttpServletRequest request, HttpServletResponse response, boolean content
            throws IOException
    {
        // Validate the requested file ------------------------------------------------------------

        // Get requested file by path info.
        String requestedFile = request.getPathInfo;

        // Check if file is actually supplied to the request URL.
        if requestedFile == null {
            // Do your thing if the file is not supplied to the request URL.
            // Throw an exception, or send 404, or show default/warning page, or just ignore it.
            response.sendErrorHttpServletResponse.SC_NOT_FOUND;
            return;
        }

        // URL-decode the file name might contain spaces and on and prepare file object.
        File file = new FilebasePath, URLDecoder.decoderequestedFile, "UTF-8";

        // Check if file actually exists in filesystem.
        if !file.exists {
            // Do your thing if the file appears to be non-existing.
            // Throw an exception, or send 404, or show default/warning page, or just ignore it.
            response.sendErrorHttpServletResponse.SC_NOT_FOUND;
            return;
        }

        // Prepare some variables. The ETag is an unique identifier of the file.
        String fileName = file.getName;
        long length = file.length;
        long lastModified = file.lastModified;
        String eTag = fileName + "_" + length + "_" + lastModified;
        long expires = System.currentTimeMillis + DEFAULT_EXPIRE_TIME;


        // Validate request headers for caching ---------------------------------------------------

        // If-None-Match header should contain "*" or ETag. If so, then return 304.
        String ifNoneMatch = request.getHeader"If-None-Match";
        if ifNoneMatch != null && matchesifNoneMatch, eTag {
            response.setStatusHttpServletResponse.SC_NOT_MODIFIED;
            response.setHeader"ETag", eTag; // Required in 304.
            response.setDateHeader"Expires", expires; // Postpone cache with 1 week.
            return;
        }

        // If-Modified-Since header should be greater than LastModified. If so, then return 304.
        // This header is ignored if any If-None-Match header is specified.
        long ifModifiedSince = request.getDateHeader"If-Modified-Since";
        if ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified {
            response.setStatusHttpServletResponse.SC_NOT_MODIFIED;
            response.setHeader"ETag", eTag; // Required in 304.
            response.setDateHeader"Expires", expires; // Postpone cache with 1 week.
            return;
        }


        // Validate request headers for resume ----------------------------------------------------

        // If-Match header should contain "*" or ETag. If not, then return 412.
        String ifMatch = request.getHeader"If-Match";
        if ifMatch != null && !matchesifMatch, eTag {
            response.sendErrorHttpServletResponse.SC_PRECONDITION_FAILED;
            return;
        }

        // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
        long ifUnmodifiedSince = request.getDateHeader"If-Unmodified-Since";
        if ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified {
            response.sendErrorHttpServletResponse.SC_PRECONDITION_FAILED;
            return;
        }


        // Validate and process range -------------------------------------------------------------

        // Prepare some variables. The full Range represents the complete file.
        Range full = new Range0, length - 1, length;
        List<Range> ranges = new ArrayList<Range>;

        // Validate and process Range and If-Range headers.
        String range = request.getHeader"Range";
        if range != null {

            // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
            if !range.matches"^bytes=\\d*-\\d*,\\d*-\\d**$" {
                response.setHeader"Content-Range", "bytes */" + length; // Required in 416.
                response.sendErrorHttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE;
                return;
            }

            // If-Range header should either match ETag or be greater then LastModified. If not,
            // then return full file.
            String ifRange = request.getHeader"If-Range";
            if ifRange != null && !ifRange.equalseTag {
                try {
                    long ifRangeTime = request.getDateHeader"If-Range"; // Throws IAE if invalid.
                    if ifRangeTime != -1 && ifRangeTime + 1000 < lastModified {
                        ranges.addfull;
                    }
                } catch IllegalArgumentException ignore {
                    ranges.addfull;
                }
            }

            // If any valid If-Range header, then process each part of byte range.
            if ranges.isEmpty {
                for String part : range.substring6.split"," {
                    // Assuming a file with length of 100, the following examples returns bytes at:
                    // 50-80 50 to 80, 40- 40 to length=100, -20 length-20=80 to length=100.
                    long start = sublongpart, 0, part.indexOf"-";
                    long end = sublongpart, part.indexOf"-" + 1, part.length;

                    if start == -1 {
                        start = length - end;
                        end = length - 1;
                    } else if end == -1 || end > length - 1 {
                        end = length - 1;
                    }

                    // Check if Range is syntactically valid. If not, then return 416.
                    if start > end {
                        response.setHeader"Content-Range", "bytes */" + length; // Required in 416.
                        response.sendErrorHttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE;
                        return;
                    }

                    // Add range.
                    ranges.addnew Rangestart, end, length;
                }
            }
        }


        // Prepare and initialize response --------------------------------------------------------

        // Get content type by file name and set default GZIP support and content disposition.
        String contentType = getServletContext.getMimeTypefileName;
        boolean acceptsGzip = false;
        String disposition = "inline";

        // If content type is unknown, then set the default value.
        // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
        // To add new content types, add new mime-mapping entry in web.xml.
        if contentType == null {
            contentType = "application/octet-stream";
        }

        // If content type is text, then determine whether GZIP content encoding is supported by
        // the browser and expand content type with the one and right character encoding.
        if contentType.startsWith"text" {
            String acceptEncoding = request.getHeader"Accept-Encoding";
            acceptsGzip = acceptEncoding != null && acceptsacceptEncoding, "gzip";
            contentType += ";charset=UTF-8";
        } 

        // Else, expect for images, determine content disposition. If content type is supported by
        // the browser, then set to inline, else attachment which will pop a save as dialogue.
        else if !contentType.startsWith"image" {
            String accept = request.getHeader"Accept";
            disposition = accept != null && acceptsaccept, contentType ? "inline" : "attachment";
        }

        // Initialize response.
        response.reset;
        response.setBufferSizeDEFAULT_BUFFER_SIZE;
        response.setHeader"Content-Disposition", disposition + ";filename=\"" + fileName + "\"";
        response.setHeader"Accept-Ranges", "bytes";
        response.setHeader"ETag", eTag;
        response.setDateHeader"Last-Modified", lastModified;
        response.setDateHeader"Expires", expires;


        // Send requested file parts to client ------------------------------------------------

        // Prepare streams.
        RandomAccessFile input = null;
        OutputStream output = null;

        try {
            // Open streams.
            input = new RandomAccessFilefile, "r";
            output = response.getOutputStream;

            if ranges.isEmpty || ranges.get0 == full {

                // Return full file.
                Range r = full;
                response.setContentTypecontentType;
                response.setHeader"Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total;

                if content {
                    if acceptsGzip {
                        // The browser accepts GZIP, so GZIP the content.
                        response.setHeader"Content-Encoding", "gzip";
                        output = new GZIPOutputStreamoutput, DEFAULT_BUFFER_SIZE;
                    } else {
                        // Content length is not directly predictable in case of GZIP.
                        // So only add it if there is no means of GZIP, else browser will hang.
                        response.setHeader"Content-Length", String.valueOfr.length;
                    }

                    // Copy full range.
                    copyinput, output, r.start, r.length;
                }

            } else if ranges.size == 1 {

                // Return single part of file.
                Range r = ranges.get0;
                response.setContentTypecontentType;
                response.setHeader"Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total;
                response.setHeader"Content-Length", String.valueOfr.length;
                response.setStatusHttpServletResponse.SC_PARTIAL_CONTENT; // 206.

                if content {
                    // Copy single part range.
                    copyinput, output, r.start, r.length;
                }

            } else {

                // Return multiple parts of file.
                response.setContentType"multipart/byteranges; boundary=" + MULTIPART_BOUNDARY;
                response.setStatusHttpServletResponse.SC_PARTIAL_CONTENT; // 206.

                if content {
                    // Cast back to ServletOutputStream to get the easy println methods.
                    ServletOutputStream sos = ServletOutputStream output;

                    // Copy multi part range.
                    for Range r : ranges {
                        // Add multipart boundary and header fields for every range.
                        sos.println;
                        sos.println"--" + MULTIPART_BOUNDARY;
                        sos.println"Content-Type: " + contentType;
                        sos.println"Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total;

                        // Copy single part range of multi part range.
                        copyinput, output, r.start, r.length;
                    }

                    // End with multipart boundary.
                    sos.println;
                    sos.println"--" + MULTIPART_BOUNDARY + "--";
                }
            }
        } finally {
            // Gently close streams.
            closeoutput;
            closeinput;
        }
    }

    // Helpers can be refactored to public utility class ----------------------------------------

    /**
     * Returns true if the given accept header accepts the given value.
     * @param acceptHeader The accept header.
     * @param toAccept The value to be accepted.
     * @return True if the given accept header accepts the given value.
     */
    private static boolean acceptsString acceptHeader, String toAccept {
        String[] acceptValues = acceptHeader.split"\\s*,|;\\s*";
        Arrays.sortacceptValues;
        return Arrays.binarySearchacceptValues, toAccept > -1
            || Arrays.binarySearchacceptValues, toAccept.replaceAll"/.*$", "/*" > -1
            || Arrays.binarySearchacceptValues, "*/*" > -1;
    }

    /**
     * Returns true if the given match header matches the given value.
     * @param matchHeader The match header.
     * @param toMatch The value to be matched.
     * @return True if the given match header matches the given value.
     */
    private static boolean matchesString matchHeader, String toMatch {
        String[] matchValues = matchHeader.split"\\s*,\\s*";
        Arrays.sortmatchValues;
        return Arrays.binarySearchmatchValues, toMatch > -1
            || Arrays.binarySearchmatchValues, "*" > -1;
    }

    /**
     * Returns a substring of the given string value from the given begin index to the given end
     * index as a long. If the substring is empty, then -1 will be returned
     * @param value The string value to return a substring as long for.
     * @param beginIndex The begin index of the substring to be returned as long.
     * @param endIndex The end index of the substring to be returned as long.
     * @return A substring of the given string value as long or -1 if substring is empty.
     */
    private static long sublongString value, int beginIndex, int endIndex {
        String substring = value.substringbeginIndex, endIndex;
        return substring.length > 0 ? Long.parseLongsubstring : -1;
    }

    /**
     * Copy the given byte range of the given input to the given output.
     * @param input The input to copy the given range to the given output for.
     * @param output The output to copy the given range from the given input for.
     * @param start Start of the byte range.
     * @param length Length of the byte range.
     * @throws IOException If something fails at I/O level.
     */
    private static void copyRandomAccessFile input, OutputStream output, long start, long length
        throws IOException
    {
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        int read;

        if input.length == length {
            // Write full range.
            while read = input.readbuffer > 0 {
                output.writebuffer, 0, read;
            }
        } else {
            // Write partial range.
            input.seekstart;
            long toRead = length;

            while read = input.readbuffer > 0 {
                if toRead -= read > 0 {
                    output.writebuffer, 0, read;
                } else {
                    output.writebuffer, 0, int toRead + read;
                    break;
                }
            }
        }
    }

    /**
     * Close the given resource.
     * @param resource The resource to be closed.
     */
    private static void closeCloseable resource {
        if resource != null {
            try {
                resource.close;
            } catch IOException ignore {
                // Ignore IOException. If you want to handle this anyway, it might be useful to know
                // that this will generally only be thrown when the client aborted the request.
            }
        }
    }

    // Inner classes ------------------------------------------------------------------------------

    /**
     * This class represents a byte range.
     */
    protected class Range {
        long start;
        long end;
        long length;
        long total;

        /**
         * Construct a byte range.
         * @param start Start of the byte range.
         * @param end End of the byte range.
         * @param total Total length of the byte source.
         */
        public Rangelong start, long end, long total {
            this.start = start;
            this.end = end;
            this.length = end - start + 1;
            this.total = total;
        }

    }

}

广告位

澳门赌场开户