精华内容
下载资源
问答
  • HTTP协议是非常重要,如果没有HTTP协议就可以说没有互联网。因为浏览所有网页,都是来源于HTTP协议支持,不但包括新闻网站,还包括购物网站,还有众多搜索网站。在Python里http.client模块提供了一些类,可以...

    HTTP协议是非常重要的,如果没有HTTP协议就可以说没有互联网。因为浏览所有网页,都是来源于HTTP协议的支持,不但包括新闻网站,还包括购物网站,还有众多的搜索网站。在Python里http.client模块提供了一些类,可以向HTTP服务器发送请求,并读取服务器的响应。

        首先要确定服务器的URL连接,然后创建HTTPConnection对象且向服务器发起连接,然后调用request向服务器发送请求,最后调用getresponse获取返回对象。通过返回对象,就可以读取服务器所响应的消息内容。

     

    整个例子的源码如下:

    #学习算法笔记-蔡军生(qq:9073204)
    #https://mysoft.blog.csdn.net/
    #2020-10-27
    
    import http.client as h
    
    
    HOST = "www.sznews.com"
    fileName = 'test_cai.jpg'
    
    # 创建 HTTPConnection对象
    conn = h.HTTPConnection(HOST)
    
    # 向服务器发送请求
    conn.request('GET', '/dolphinfile/sznews/2020/08/2502_1_15965270655766.jpg')
    
    #获取HTTPResponse对象
    r = conn.getresponse()
    
    #读取下载的数据,并存入本地文件
    with open(fileName, mode = 'wb') as f:
        data = bytearray(4096)
        while r.readinto(data) > 0 :
            f.write(data)
    
    print("下载完成")
    
    
    
    
    
    
     
    

    结果如下:

    ================= RESTART: D:/work/ITA/src/im_tkinter_30.py =================

    下载完成

     

    展开全文
  • OkHttp(GitHub主页https://github.com/square/okhttp)是近来人气攀升一款安卓第三方HTTP包,这里我们来讲解一下如何使用AndroidOkHttp包实现基于HTTP协议的文件上传下载:
  • FTP 是File Transfer Protocol(文件传输协议)英文简称,从这个名字也能看出来,这个协议是为了文件传输而生使用前需要下载一个commons-net-3.3.jar 直接看上传代码 /** * 向FTP服务器上传文件 * * ...

    FTP 是File Transfer Protocol(文件传输协议)的英文简称,从这个名字也能看出来,这个协议是为了文件传输而生的。

    使用前需要下载一个commons-net-3.3.jar

    直接看上传代码

    /** 
         * 向FTP服务器上传文件 
         *  
         * @param url 
         *            FTP服务器hostname 就是ip
         * @param port 21
         *            端口默认80 
         * @param username 
         *            用户名 
         * @param password 
         *            密码 
         * @param path 
         *            FTP服务器保存目录
         * @param filename
         *            文件名称 上传到FTP服务器上的文件名,是自己定义的名字 
         * @param input 
         *            文件输入流 
         * @return 
         */  
        public static boolean upload(String url, int port, String username,  
                String password, String path, String filename, InputStream input) {  
            
            boolean success = false;  
            FTPClient ftp = new FTPClient();  
            
            try {  
                ftp.setDataTimeout(2000);//设置连接超时时间  
                ftp.connect(url, port); 
                // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器  
                ftp.login(username, password); 
                if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {  
                    ftp.disconnect();  //未连接到FTP,用户名或密码错误
                    return success;  
                }  
                
                boolean isExist =  createDirecroty(path,ftp) ;  
                if(!isExist){  
                    return success;  
                }  
                
                //获取服务器当前目录指定文件信息
                FTPFile[] files = ftp.listFiles(filename);
                if(files.length > 1){
                    ftp.deleteFile(filename);
                }
                  
                ftp.setControlEncoding("UTF-8");
                ftp.setBufferSize(1024);  
                ftp.enterLocalPassiveMode();    
                ftp.setFileType(FTP.BINARY_FILE_TYPE);  
                //处理中文名称的文件名,如果不加这一句的话,中文命名的文件是不能上传的  
                filename = new String(filename.getBytes("GBK"), "iso-8859-1") ;  
                ftp.storeFile(filename, input);  
      
                input.close();  
                ftp.logout(); 
                success = true;  
            } catch (IOException e) {
                e.printStackTrace();  
            } finally {  
                if (ftp.isConnected()) {  
                    try {  
                        ftp.disconnect();  
                    } catch (IOException ioe) {  
                    }  
                }  
            }  
            return success;  
    
        }  
    
     /**
         * 递归创建远程服务器目录
         * 
         * @param remote
         *            远程服务器文件绝对路径
         * @return 目录创建是否成功
         * @throws IOException
         */
        public static boolean createDirecroty(String remote,FTPClient ftp) throws IOException {
            
            String totalPath = "";
            boolean success = true;
            String[] path = remote.split("/");
            for(int i=0; i<path.length; i++){            
                String father = path[i];
                if(father == null || "".equals(father)){
                    success = false;
                    break;
                }
                totalPath = totalPath + "/"+father;
                try {
                    boolean isExit = ftp.changeWorkingDirectory(totalPath);
                    if(!isExit){
                        boolean make = ftp.makeDirectory(totalPath);
                        if(!make){
                            success = false;
                            break;
                        }
                        //工作路径切换到此
                        boolean change = ftp.changeWorkingDirectory(totalPath);
                        if(!change){
                            success = false;
                            break;
                        }
                    }
                } catch (IOException e) {
                    success = false;
                    break;
                }
            }  
            return success;
    
        }

    流程其实很简单的,就是先连接FTP服务器(这里要求你的服务器支持FTP协议),再登陆,然后再创建相关目录,记住目录要一级一级创建,然后就是通过ftp上传了。这里其实看不到上传进度,就下来看另外一个方法

    public static boolean upload(String url, int port, String username,  
                String password, String remotePath, File localFile) {  
            
            boolean success = false;  
            RandomAccessFile raf = null;
            OutputStream output = null;
            FTPClient ftp = new FTPClient();  
            
            try {  
                ftp.connect(url, port); 
                // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器  
                ftp.login(username, password); 
                int reply = ftp.getReplyCode();
                if (!FTPReply.isPositiveCompletion(reply)) {  
                    ftp.disconnect();  //未连接到FTP,用户名或密码错误
                    return success;  
                }  
                
                boolean isExist =  createDirecroty(remotePath,ftp) ;  
                if(!isExist){  
                    return success;  
                }  
                
                //获取当前目录指定文件信息
                FTPFile[] files = ftp.listFiles(localFile.getName());
                if(files.length > 1){
                    ftp.deleteFile(localFile.getName());
                }
                  
                raf = new RandomAccessFile(localFile, "r");  
                long serverSize = 0;
                /* enterLocalPassiveMode 
                 * 每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据。
                 * 为什么要这样做呢,因为ftp server可能每次开启不同的端口来传输数据,
                 * 但是在linux上或者其他服务器上面,由于安全限制,可能某些端口没有开启,
                 * 所以就出现阻塞
                 * */
                ftp.enterLocalPassiveMode();  
                ftp.setFileType(FTP.BINARY_FILE_TYPE);//设置二进制传输,还支持传输ACSII数据  
                ftp.setRestartOffset(serverSize);  
                raf.seek(serverSize);  
                
                // 进度  
                long localSize = localFile.length(); // 本地文件的长度 
                long step = localSize / 100;  
                long process = 0;  
                long currentSize = 0; 
                String filename = localFile.getName();
                filename = new String(filename.getBytes("GBK"), "iso-8859-1") ;  
                output = ftp.appendFileStream(filename);  
                byte[] b = new byte[1024];  
                int length = 0;  
                while ((length = raf.read(b)) != -1) {  
                    output.write(b, 0, length);  
                    currentSize = currentSize + length;  
                    if (currentSize / step != process) {  
                        process = currentSize / step;  
                        Log.e(TAG, "上传进度:" + process);
                    }  
                }  
                 
                if (ftp.completePendingCommand()) {
                    success = true; 
                    Log.e(TAG, "文件上传成功");
                } 
      
            } catch (IOException e) {
                e.printStackTrace();  
            } finally {  
                
                try {
                    output.flush();
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }  
                
                try {
                    raf.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                } 
                
                try {
                    ftp.logout();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                if (ftp.isConnected()) {  
                    try {  
                        ftp.disconnect();  
                    } catch (IOException ioe) {  
                    }  
                }  
            }  
            return success;  
    
        }

    这里就是通过appendFileStream方法获取输出流来进行进度获取。

    还可以通过ftp.setRestartOffset(serverSize);  raf.seek(serverSize);  来进行断点上传

    上传结束,再来看下载

    public static boolean downloadFile(String url, int port, String username,  
                String password, String localPath, String serverPath){  
            
            boolean success = false;  
            FTPClient ftpClient = new FTPClient();
     
            try {
                ftpClient.setControlEncoding("GBK"); 
                ftpClient.connect(url, port);
                ftpClient.login(username, password);
                if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {  
                    ftpClient.disconnect();  //未连接到FTP,用户名或密码错误
                    return false;    
                }
                
                // 先判断服务器文件是否存在  
                FTPFile[] files = ftpClient.listFiles(serverPath);  
                if (files.length == 0) {  
                    Log.e(TAG,"服务器文件不存在 serverPath="+serverPath);  
                    return false;  
                }  
                
                localPath = localPath + files[0].getName();  
                long serverSize = files[0].getSize(); // 获取远程文件的长度  
                File localFile = new File(localPath);  
                long localSize = 0;  
                if (localFile.exists()) {  
                    localFile.delete(); 
                }  
                // 进度  
                long step = serverSize / 100;  
                long process = 0;  
                long currentSize = 0;  
                // 开始准备下载文件  
                ftpClient.enterLocalActiveMode();  
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);  
                OutputStream out = new FileOutputStream(localFile, true);  
                ftpClient.setRestartOffset(localSize); //设置从哪里开始下,就是断点下载 
                InputStream input = ftpClient.retrieveFileStream(serverPath);  
                byte[] b = new byte[1024];  
                int length = 0;  
                while ((length = input.read(b)) != -1) {  
                    out.write(b, 0, length);  
                    currentSize = currentSize + length;  
                    if (currentSize / step != process) {  
                        process = currentSize / step;  
                        Log.e(TAG,"下载进度:" + process);   
                    }  
                }  
                out.flush();  
                out.close();  
                input.close();  
                // 此方法是来确保流处理完毕,如果没有此方法,可能会造成现程序死掉  
                if (ftpClient.completePendingCommand()) {  
                    Log.e(TAG,"文件下载成功");  
                    success = true;
                } 
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return success;
    
        }

    这里就是通过retrieveFileStream获取输入流进行下载。

     

    再看看使用HTTP进行文件上传

        /**
         * 通过拼接的方式构造请求内容,实现参数传输以及文件传输
         * 
         * @param url http://192.168.1.19:8080/web/servlet/UploadServlet
         * @param params text content
         * @param files pictures
         * @return String result of Service response
         * @throws IOException
         */
        public static String post(String url, Map<String, String> params, Map<String, File> files)
                throws IOException {
            String BOUNDARY = java.util.UUID.randomUUID().toString();
            String PREFIX = "--";
            String LINEND = "\r\n";
            String MULTIPART_FROM_DATA = "multipart/form-data";
            String CHARSET = "UTF-8";
    
            URL uri = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
            conn.setReadTimeout(10 * 1000); 
            conn.setDoInput(true);// 允许输入
            conn.setDoOutput(true);// 允许输出
            conn.setUseCaches(false); // 不允许使用缓存
            conn.setRequestMethod("POST");
            conn.setRequestProperty("connection", "keep-alive");
            conn.setRequestProperty("Charsert", "UTF-8");
            conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY);
            conn.setChunkedStreamingMode(1024);
    
            // 首先组拼文本类型的参数
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                sb.append(PREFIX);
                sb.append(BOUNDARY);
                sb.append(LINEND);
                sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND);
                sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
                sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
                sb.append(LINEND);
                sb.append(entry.getValue());
                sb.append(LINEND);
    
            }
    
            DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
            outStream.write(sb.toString().getBytes());
            // 发送文件数据
            if (files != null)
                for (Map.Entry<String, File> file : files.entrySet()) {
                    StringBuilder sb1 = new StringBuilder();
                    sb1.append(PREFIX);
                    sb1.append(BOUNDARY);
                    sb1.append(LINEND);
                    sb1.append("Content-Disposition: form-data; name=\"uploadfile\"; filename=\""
                            + file.getValue().getName() + "\"" + LINEND);
                    sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND);
                    sb1.append(LINEND);
                    outStream.write(sb1.toString().getBytes());
    
                    InputStream is = new FileInputStream(file.getValue());
                    byte[] buffer = new byte[1024];
                    int len = 0;
                    while ((len = is.read(buffer)) != -1) {
                        outStream.write(buffer, 0, len);
                    }
                    is.close();
                    outStream.write(LINEND.getBytes());
                }
    
            // 请求结束标志
            byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
            outStream.write(end_data);
            outStream.flush();
            // 得到响应码
    
            int res = conn.getResponseCode();
    
           //获取服务器返回结果
    
           InputStream in = conn.getInputStream();
            StringBuilder sb2 = new StringBuilder();
            if (res == 200) {
                int ch;
                while ((ch = in.read()) != -1) {
                    sb2.append((char) ch);
                }
            }
            outStream.close();
            conn.disconnect();
            return sb2.toString();
        }
    
    //调用方式如下
    
    public String uploadFile(File f){
            final Map<String, String> params = new HashMap<String, String>();
            params.put("sessionId", "123456");
            final Map<String, File> files = new HashMap<String, File>();
            files.put("uploadfile",f);
            String response=null;
            try {
                response = UploadUtil.post(FAULT_FILEUPLOAD_URL, params, files);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return response;
        }

    使用HTTP进行文件下载其实就是获取输入流数据,然后通过输出流写到文件里,主要代码跟上面FTP下载差不多

     

    至于使用哪个协议下载好,就要看具体情况了,对个人而言没啥区别

    FTP协议是专门用于文件传输的协议,网络传输更加安全

    HTTP只是超文本传输协议,最原始的目的仅仅只是为了将超文本的信息(包括图象视频等)传到你的机子上就行了,但是使用方便

    展开全文
  • 值得注意是,zmodem协议不能处理超过4G的文件。 Mac brew install lrzsz lrzsz是对zmodem协议实现软件包。 iterm2-zmodem https://github.com/mmastrac/iterm2-zmodem 这里主要是下载两个脚本: iterm2-r...

    背景

    CentOS7默认支持zmodem协议,进行文件传输。具体可以看一看百科:ZMODEM。值得注意的是,zmodem协议不能处理超过4G的文件

    Mac

    brew install lrzsz
    

    lrzsz是对zmodem协议实现的软件包。

    iterm2-zmodem

    https://github.com/mmastrac/iterm2-zmodem
    这里主要是下载两个脚本:

    • iterm2-recv-zmodem.sh
    • iterm2-send-zmodem.sh

    下载完成后,将这个两个脚本负责到/usr/local/bin/文件夹下面,具体如下:

    zylMBP:Downloads zhangyalin$ unzip iterm2-zmodem-master.zip
    Archive:  iterm2-zmodem-master.zip
    f2b05887e8cea060d28c7129dbad8e3ec283a2c5
       creating: iterm2-zmodem-master/
      inflating: iterm2-zmodem-master/README.md
      inflating: iterm2-zmodem-master/iterm2-recv-zmodem.sh
      inflating: iterm2-zmodem-master/iterm2-send-zmodem.sh
    zylMBP:Downloads zhangyalin$ cd iterm2-zmodem-master
    zylMBP:iterm2-zmodem-master zhangyalin$ ls -l
    total 24
    -rw-r--r--@ 1 zhangyalin  staff  1687 May 24 00:55 README.md
    -rwxr-xr-x@ 1 zhangyalin  staff  1177 May 24 00:55 iterm2-recv-zmodem.sh
    -rwxr-xr-x@ 1 zhangyalin  staff  1116 May 24 00:55 iterm2-send-zmodem.sh
    zylMBP:iterm2-zmodem-master zhangyalin$ cp iterm2-recv-zmodem.sh /usr/local/bin/iterm2-recv-zmodem.sh
    zylMBP:iterm2-zmodem-master zhangyalin$ cp iterm2-send-zmodem.sh /usr/local/bin/iterm2-send-zmodem.sh
    

    iTerm2

    设置iTerm2两个触发器,如下:

    Key Value
    Regular expression rz waiting to receive.**B0100
    Action Run Silent Coprocess
    Parameters /usr/local/bin/iterm2-send-zmodem.sh
    Instant checked
    Key Value
    Regular expression **B00000000000000
    Action Run Silent Coprocess
    Parameters /usr/local/bin/iterm2-recv-zmodem.sh
    Instant checked

    具体如图:

    iTerm2触发器配置

    iterm2两个触发器具体配置

    使用

    远程ssh登录到CentOS7后,就可以直接使用rz进行文件上传和sz进行文件下载。

    不足

    mmastrac大神将在未来为这个iTerm2的上传下载插件补充进度条功能。虽然,这个协议有4G文件大小约束,不过,可以使用split和cat命令来,切割和合并文件。

    参考

    ZModem integration for iTerm 2

    lrzsz: free x/y/zmodem implementation

    ZMODEM

    Linux & Mac 对文件进行 split 分割与 cat 合并

    展开全文
  • http协议及基于http协议的文件下载

    千次阅读 2019-10-11 12:27:41
    5. 基于HTTP协议的文件下载 5.1 文件整体下载 5.2 文件分段(Range)下载 5.2.1 获取文件的大小 5.2.2 下载分段文件 5.3 文件分块(chunk)下载 1. HTTP 协议概述 日常我们使用网络用得最多无疑是在Web ...

    目录

    1. HTTP 协议概述

    2. URL 与资源

    3. HTTP报文

    4. 使用Postman 获取数据

    5. 基于HTTP协议的文件下载

    5.1 文件整体下载

     5.2 文件分段(Range)下载

    5.2.1 获取文件的大小

    5.2.2 下载分段文件

    5.3 文件分块(chunk)下载


    1. HTTP 协议概述

    日常我们使用网络用得最多的无疑是在Web 浏览器(下文统一使用浏览器)上查找资料、看视频、看书、看新闻等等,而在浏览器中只需要输入一些搜索就可以得到想要的信息,这归根于搜索引擎的好处,但是实际上每个网页其实由多个资源组成。我们的浏览器就是一个HTTP 客户端,通过HTTP 协议访问服务器,得到服务器中的HTML 页面、文本文件、图片、音频等资源,并且将这些资源搬运到浏览器(客户端)显示给我们。

    HTTP 协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传输协议,它是基于TCP/IP 协议通信的,因此它也是基于<客户端-服务器>模型运作的,是一个应用层协议,可以用它来传输服务器的各种资源,如文本、图片、音频等。

    HTTP 协议的特点:

    1. 简单:当客户端向服务器请求服务时,只需传送请求方法和路径即可获取服务器的资源,请求方法常用的有GET、HEAD、POST 等,每种方法规定了客户端与服务器通信的类型不同。

    2. 快捷:由于HTTP 协议简单,使得HTTP 服务器的程序规模小,因而通信速度很快。

    3. 灵活:HTTP 允许传输任意类型的数据对象,传输的类型由Content-Type 加以标记。

    4. 无连接:无连接的含义是限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接,简单来说就是每进行一次HTTP 通信,都要断开一次TCP 连接,可随着HTTP 的普及,文档中包含大量图片的情况多了起来,每次请求完都要断开TCP 连接,无疑增加通信量的开销,为了解决TCP的连接问题,HTTP1.1 提出了持久连接的方法,即任意一端只要没有明确提出断开连接,则保持TCP 连接状态,这样子就就减少了TCP 连接的重复建立和断开所造成的额外开销,减轻了服务端的负载。注意,在HTTP1.1 版本之后才出现持久连接的方法

    5. 无状态:HTTP 协议是无状态协议,无状态是指协议对于事务处理没有记忆能力,即HTTP 协议无法根据之前的状态进行本次的请求处理,这就意味着如果后续处理需要前面的信息,它必须重传数据,这样的情况可能导致HTTP 协议传输的数据量增大,当然,凡事都有两面性,在另一方面,在服务器不需要先前信息时它的应答就较快,可以减少服务器的资源消耗,其实这种无状态对于用户来说也是不友好的,因此为了解决无状态的问题,引入了Cookie 技术,这是一种可以让服务器知道用户上一次做了什么操作,并且记录下来,它是存储在客户端之中的,比如我们在淘宝上买东西,我们选择了几个商品,但是到了结账会跳转到另一个页面,此时如果服务器不知道我们选择了哪些商品,那怎么能结账成功呢?所以Cookie 就是用来绕开HTTP 的无状态性的“手段”之一,服务器可以设置或读取Cookies 中包含信息,让服务器知道我们选择了什么商品,借此维护用户跟服务器会话中的状态,当然,Cookie 会被加密存储在客户端中,直到过期或者手动清除。

    2. URL 与资源

    我们可以把整个英特网看做是一个巨大的图书馆,里面的资源应有尽有,并且是对我们是开放的,我们想要找一本书,那么我们就需要直到他存放在哪里,然后去找到它。网络中的资源也是应有尽有,那么怎么样才能在网络的海洋中找到我们想要的资源呢?因此URI(Uniform Resource Identifiers)就被设计出来,用于统一管理资源,就像我们去图书馆找书一样,我们必须通过图书馆的系统,找到书所在的位置,而不是让我们自己一本一本书去找。

    URL 全称是Uniform Resource Locator,中文叫统一资源定位符,是互联网上用来标识某一处资源的绝对地址,使用它我们就必然能找到资源,除非资源已经被转移了。URI 是一个通用的概念,由两个子集组成,分别是URL 和URN,URL 是通过资源的位置来标识资源,而URN 更高级一点,只需通过资源名字即可识别资源,与他们所处的位置是无关的,目前暂时还未推广URN。

    大部分URL 都会遵循URL 的语法,一个URL 的组成有多个不同的组件,一个URL的通用格式如下

    <scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

    当然,绝大部分的URL 是不会包含所有组件的内容的.

    URL 组件
    scheme(方案) 指定访问服务器获取资源时使用哪种协议,有HTTP、HTTPS、FTP、SMTP 等协议。
    user(用户) 某些方案访问资源时候需要指定用户名,才有权限获取资源。
    password(密码) 用户名后面可能需要密码进行验证,用户名与密码直接使用 “:” 冒号分隔连接。
    host(主机) 资源宿主服务器的主机名或者IP 地址(点分十进制)。
    port(端口) 资源宿主服务器正在监听的端口号,很多方案都有默认的端口号,而无需我们自己填写,比如HTTP 默认使用80 端口,HTTPS 默认使用443 端口。端口不是一个URL 必须的部分,如果省略端口部分,将采用默认端口。
    path(路径) 服务器本地资源的路径。类似于电脑中的文件路径一样,使用“/”将路径与端口隔离。从域名后的第一个“/”开始到最后一个“/”为止,虚拟目录也不是一个URL 必须的部分,在路径之后是需要一个文件名,这就是URL 指定的资源。文件名部分也不是一个URL 必须的部分,如果省略该部分,则使用默认的文件名
    params(参数) 某些方案会使用这个组件来输入参数,可以拥有多个参数,使用“;”符号与路径分隔开。
    query(查询) 某些方案会使用这个组件传递参数以激活应用程序,查询组件的内容没有通用的格式,用“?”字符与其他组件分隔开
    frag(片段) 一小片或者一部分资源的名字,引用对象时,不会将片段组件内容传输给服务器,这个字段是在客户端内部使用的,通过“#”字符与其他组件分隔开。

    比如:https://blog.csdn.net/XieWinter,它就是一个URL,这是最简单的一个URL 格式,通过其可以访问相应的资源。

    3. HTTP报文

    HTTP 报文是由3 个部分组成,分别是:对报文进行描述的“起始行”,包含属性的“首部”,以及可选的“数据主体”,对于请求报文与应答报文,只有“起始行”的格式是不一样的。

    http 请求报文

    <method> <request-url> <version>                    // 起始行
    <headers>                                           // 首部
    
    <entity-body>                                       // 数据主体
    

    http 应答报文

    <version> <status> <reason-phrase>                  // 起始行
    <headers>                                           // 首部
    
    <entity-body>                                       // 数据主体
    

    起始行和首部就是由行分隔的 ASCII 文本组成,每行都以由两个字符组成的行终止序列作为结束,其中包括一个回车符(ASCII 码 13)和一个换行符(ASCII 码 10), 这个行终止序列可以写做 CRLF。

    这两种HTTP 报文的各个部分简单描述:

    • 方法(method):HTTP 请求报文的起始行以方法作为开始,方法用来告知服务器要做些什么,常见的方法有GET、POST、HEAD 等,比如“GET /forum.php HTTP/1.1” 使用的就是GET 方法。
    • 请求URL(request-URL):指定了所请求的资源。
    • 版本(version):指定报文所使用的HTTP 协议版本,其中<major>指定了主要版本号, <minor>指定了次要版本号,它们都是整数,其格式如下:
    HTTP/<major>.<minor>
    • 状态码(status):这是在HTTP 应答报文中使用的,状态码是在每条响应报文的起始行中返回的一个数字码,描述了请求过程中所发送的情况,比如成功、失败等,不同的状态码有不同的含义

    200 常见的返回OK,404错误等,206 状态码将会文件分段下载中使用。

    • 原因短语(reason-phrase):这其实是给我们看的原因短语,因为数字是不够直观,它只是状态码的一个文本形式表达而已。 
    • 首部(header):HTTP 报文可以有0 个、1 个或者多个首部,HTTP 首部字段向请求和响应报文中添加了一些附加信息,从本质上来说,它们是一个<名字:值>对,每个首部都包含一个名字,紧跟着一个冒号“:”,然后是一个可选的空格,接着是一个值,最后以CRLF 结束,比如“Host: blog.csdn.net”就是一个首部。
    • 数据主体(entity-body):这部分包含一个由任意数据组成的数据块,其实这与我们前面所讲的报文数据区域是一样的,用于携带数据,HTTP 报文可以承载很多类型的数字数据:图片、视频、音频、HTML 文档、软件应用程 序等。

    4. 使用Postman 获取数据

    既然了解了HTTP 协议与HTTP 报文的相关知识,我们就来使用Postman 软件了解一下HTTP 协议的传输过程。

    官网:https://www.getpostman.com/

    安装后打开软件,就可以使用软件进行测试HTTP 协议,可以很直观看到发送的HTTP 报文是什么,也能很直观看到响应的数据。

    可以简单注册一下,就可以记录发送的命令,相当不错。

    或者

    查看发送HTTP 请求报文

      Postman 功能很强大,只是用到其最简单的功能。

    5. 基于HTTP协议的文件下载

    5.1 文件整体下载

    文件整体下载比较简单,只需要知道资源的位置下载就可以。

     5.2 文件分段(Range)下载

    在嵌入式设备或者资源有限的设备中,可能需要下载的文件的大小,自身资源不能一次性的满足,需要分段下载才行。文件分段下载的前提条件是,支持文件分段下载,否则,即便采用分段获取的,下载的也是整个文件大小,比如GitHub上面的就会下载整个文件的大小。

    文件下载的流程如下:

    • 获取文件的大小
    • 下载各分段文件
    • 文件拼接
    • 文件完整性检查

    5.2.1 获取文件的大小

    因为需要分段下载,因此,首先需要知道文件的大小,才方便计算分段,当然,不分段也不是不可以,但处理起来,没有先获取文件大小规范。

    可以直接获取资源链接的网站,检查响应的消息头字段中是否存在 如下字段,存在则支持,否则,不支持

    Accept-Ranges: bytes

    这就需要用到消息头字段添加 Range 字段

    
    Range: bytes=0-1024 获取最前面1025个字节
    Range: bytes=-500   获取最后500个字节
    Range: bytes=1025-  获取从1025开始到文件末尾所有的字节
    Range: 0-0          获取第一个字节
    Range: -1           获取最后一个字节

    请求成功后服务器会返回状态码206, 并返回如下字段指示返回结果, 0-1024指示返回分段范围, 7877指示文件总大小
    Content-Range: bytes 0-1024/7877

    因此采用如下,获取文件大小:

    Range: 0-0          获取第一个字节

    示例:

    5.2.2 下载分段文件

    分段下载分为两种,一种就是一次请求一个分段,一种就是一次请求多个分段。

    下面两种都会有介绍,但是,实际上,除非为了多线程多段下载(PC下载软件 Internet Download Manager ),否则,在嵌入式开发中一般还是采用一次请求一个分段。

    根5.2.1 可知文件大小,如示例文件大小为 3700512 字节。

    具体怎么分段,根据设备资源来分配,原理是一样都。

    假如分三段 0-999999,1000000-1999999,2000000-3700511

    一次请求一个分段

    • 第一段消息头字段添加
    Range: bytes=0-999999 获取最前面1000000个字节
    

     

     

    • 第二段消息头字段添加

    Range: bytes=1000000-1999999 获取第二个1000000个字节
    

     

     

    • 末段消息头字段添加

    Range: bytes=2000000-3700511 获取最后一段
    或另一种方式
    
    Range: bytes=2000000- 获取最后一段             // 这种更好些

     

     一次请求多个分段

     至此,文件下载完成,文件的拼接,如果是 类似都 xxx.bin文件,那么保存的时候,同一块地址连续保存就完成来拼接来。因为这里主要也是想表达通过http 服务分段下载文件,来升级程序,实现 FOTA 功能 ,其它方法暂不多说;

    文件下载完成后,需要校验文件都完整性,否则不完整都程序,一旦升级将造成死机的情况。

    5.3 文件分块(chunk)下载

    Transfer-Encoding: chunked 表示输出的内容长度不能确定,普通的静态页面、图片之类的基本上都用不到这个。但动态页面就有可能会用到,但也注意到大部分asp,php,asp.net动态页面输出的时候大部分还是使用Content-Length,没有使用Transfer-Encoding: chunked。

    Transfer-Encoding: chunked使用,就不必申请一个很大的字节数组了,可以一块一块的输出,更科学,占用资源更少。

    这在http协议中也是个常见的字段,用于http传送过程的分块技术,原因是http服务器响应的报文长度经常是不可预测的,使用Content-length的实体搜捕并不是总是管用。分块技术的意思是说,实体被分成许多的块,也就是应用层的数据,TCP在传送的过程中,不对它们做任何的解释,而是把应用层产生数据全部理解成二进制流,然后按照MSS(Maxitum Segment Size 最大分段大小,一般客户端资源有限,大小受其限制)的长度切成一分一分的,一股脑塞到tcp协议栈里面去,而具体这些二进制的数据如何做解释,需要应用层来完成,所以在这之前,一块整体应用层的数据需要等它分成的所有TCP  segment到达对方,重新组装后,应用程序才使用自己的解码方法还原它们。

    HTTP1.1采用了持久的连接,也就是一次TCP的连接不马上释放,允许许多的请求跟响应在一个TCP的连接上发送,所以客户机与服务器需要某种方式来标示一个报文在哪里结束和在下一个报文在哪里开始。简单的方法是使用呢content-length,但这只有当报文长度可以预先判断的时候才起作用,而对于动态的内容或者在发送数据前不能判定长度的情况下,可以使用分块的方法来传送编码。

    Web服务器有时生成HTTPResponse无法在Header就确定消息大小的,这时一般来说服务器将不会提供Content-Length的头信息,而采用Chunked编码动态的提供body内容的长度。进行Chunked编码传输的HTTP Response会在消息头部设置:Transfer-Encoding: chunked表示Content Body将用Chunked编码传输内容。

     

     

     Chunked编码使用若干个Chunk串连而成,由一个标明长度为0的chunk标示结束。每个Chunk分为头部和正文两部分,头部内容指定下一段正文的字符总数(十六进制的数字)和数量单位(一般不写),正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF)隔开。在最后一个长度为0的Chunk中的内容是称为footer的内容,是一些附加的Header信息(通常可以直接忽略)。

     

    chunk编码其实是一种动态数据传输协议,针对大数据可以动态传输,网页可以动态显示。

    chunk编码格式如下:

    [chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]

    chunk size是以十六进制的ASCII码表示,比如33 37 35,计算成长度应该是:0x375(885),表示从回车之后有连续的885字节的数据。

    chunk数据以0长度的chunk块结束。

    分块传输的前提条件是,服务器支持该方式!!!且为http1.1协议。

     

    注:本节没有详细介绍HTTP及其消息头字段,只是从FOTA都角度,来说明相关都东西,深入都需要查询相关协议。

    展开全文
  •  基于HTTP协议的客户/服务器模式信息交换过程,它分四个过程:建立连接、发送请求信息、获取服务器响应状态、关闭连接。其中较为复杂过程是:发送请求信息、获取服务器响应状态.  1)向服务器发送请求信息 ...
  • 本文根据netty官方文档,实现了一个文件下载的简单德莫、。相比较tomcat里面跑基于ssmweb程序,netty控制粒度非常小,也非常细。很多细节需要我们自己掌控。但是好处是netty执行效率高,对大文件的支持好...
  • HTTP协议是一种为了将位于全球各个地方的Web服务器中的内容发送给不特定多数用户而制订的协议。也就是说,可以把HTTP看作是旨在向不特定多数的用户“发放”文件的协议。HTTP使用于从服务器读取Web页面内容。Web浏览器...
  • 之前一篇博文详细描述了使用TIdhttp控件下载http协议的文件,在我项目的使用过程中发现对于下载Https协议的文件与Http协议的文件不同,毕竟Https在HTTP协议基础上增加了SSL协议。接下来我们就来看看如何下载...
  • 这几天都在做关于使用Socket然后使用Tcp协议去像服务端请求下载文件。 总结一下:由于之前没有接触过这种使用socket来像服务端请求文件导致走了不少弯路。 基于Tcp协议的Socket,我本来纠结点是在于socket....
  • 今天忘记待USB线了,也不想回去拿,就想能不能借助uboot和... 你只需要 支持串口协议的UBOOT(一般UBOOT起来都是已经有了相关串口传输协议),串口线,PC上有minicom,我PC直接装是UBUNTU系统,PC上虚拟机跑U
  • EiskaltDC ++ –使用DC和ADC协议共享文件 ... PFSR(部分文件共享)支持:在文件下载过程中,即使没有人没有完全下载文件,用户也可以互相下载文件的一部分。 支持DHT(允许通过TTH搜索文件并交换这些文件而无
  • 下面此种方式是我刚刚使用vscode常用打开方式,但是在使用Axios时候就会出现一些问题【问题如下】。好像是跨域问题。 但是使用idea执行一样代码,就可以得到结果。 仔细一看,是两者访问方式不一样,一...
  • 使用PHP下载文件

    2017-12-17 20:09:00
    使用PHP脚本来下载文件,无非是通过两种方式,一种是使用system、exec等即有函数调用...使用Socket下载文件,首先如果是http协议的文件,必须明白HTTP协议的运行过程,如果是FTP协议的则要了解ftp协议运行过程,比...
  • wget是一个从网络上自动下载文件的自由工具。它支持HTTP,HTTPS和FTP协议,可以使用HTTP代理.所谓自动下载是指,wget可以在用户退出系统之后在后台执行。这意味这你可以登录系统,启动一个wget下载任务,然后...
  • Android利用SMB协议,查看文件或者下载文件 1.说明:假设现在手机需要再局域网下,下载电脑上文件或者同局域网内其他存储设备上文件,(存储设备有SMBA服务)。 2.用到资源 JCIFS项目:...
  • qTox是使用加密对等Tox协议的聊天,语音,视频和文件传输即时消息客户端。 | | | | IRC: 视窗 Linux OS X FreeBSD , , , , , , 每晚, , , 建议使用粗体。 除安装程序/软件包以外内部版本不会...
  • 或许对 Java 程序员来说,HTTP 协议并不陌生,他们使用的客户端几乎就是浏览器。如果用 C 实现客户端的话,不了解 HTTP 是无法与 HTTP 服务器或者物联网平台通讯以及进行业务处理。 本场 Chat 首先通过文件的上传和...
  • 第1天http协议介绍+文件的上传和下载一. http协议介绍:二.请求协议和响应协议1.请求协议:2.响应协议:三.使用fiddler进行http抓包工具查看 详细信息 :该软件以后经常用到四.http请求协议①请求首行: 一. http...
  • 利用阿里云虚拟主机,上传与下载文件 二、需要资料 1、阿里云虚拟主机:FTP登录主机地址、FTP登录用户名、FTP登录密码 获取指南:指导链接 2、FileZilla 下载链接:下载链接 注:如果你自己电脑系统...
  • wget是一个从网络上自动下载文件的自由工具。它支持HTTP,HTTPS和FTP协议,可以使用HTTP代理. 所谓自动下载是指,wget可以在用户退出系统之后在后台执行。这意味这你可以登录系统,启动一个wget下载任务,...
  • 下载下来的文件是一个压缩包,解压出来,你是找不到一个文件来打开进行界面操作。 需使用命令编译。 编译步骤 原文链接:http://blog.csdn.net/everandforever/article/details/6102781
  • Spring boot 使用FTP协议传输文件

    千次阅读 2019-01-29 11:25:52
    FTP方式必须事先开发特定采集程序进行日志数据采集,每次连接都是完整下载整个日志文本文件,网络传输数据量可能非常大,属于主动采集日志数据方式。 想要通过FTP协议传输文件,需先在FTP服务器配置FTP协议: ...
  •  网络编程中文件的上传下载是最常见场景,本着不重复造轮子原则,日常工作如果遇到相关问题我们首先想到可能是从网上找现成代码直接拿来用,很少去关心具体是如何实现,可能也是没时间去研究别人如何实现...
  • Python paramiko SFTP协议上传下载文件

    千次阅读 2016-08-29 12:30:31
    描述 业务有一个发货流程,人工...首先遇到问题是在Windows上与linux服务器交互,使用SFTP协议(SSL加密FTP协议,类似于HTTPSPS:个人理解!)上传下载文件 安装 pip install paramiko 代码demo import paramikoho
  • 在ubuntu环境下,局域网内可以使用nc来传送文件,也可以使用基于Http协议的方式来下载文件我们可以使用python -m SimpleHTTPServer ${port}来启动服务默认端口是8000,另外我们也可以指定端口,比如:python -m ...
  • 最近使用了HTTPS协议在Android设备上...1.下面的代码是关于文件上传和下载需要使用的工具类。 package com.example.justyoung.logintest; import android.content.SharedPreferences; import android.util.Log; imp

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,057
精华内容 2,422
关键字:

下载文件使用的协议是