如何在PHP中分块读取文件流,也就是说当使用 fopen打开一个文件以后,如何能分块读取文件流?
 PS: 由于要分块上传文件,因此需要将文件进行拆分,目前已有的办法是分块读取到字符串中,然后写入临时文件,  想了解有没有办法能直接分块读取文件流,而不使用临时文件? 谢谢!

解决方案 »

  1.   

    有关“断点续传”的资料过两天找给你你有:由于要分块上传文件,因此需要将文件进行拆分,目前已有的办法是分块读取到字符串中,然后写入临时文件组装时就一定知道那个临时文件装配在哪里了
    一就是说你在上传时就一定有标志,标明本次上传的块叫要被转配在哪里的初次用 w 方式打开文件
    以后按 r+ 方式打开文件, 用 fseek 定位到相应位置去写就可以了
      

  2.   

    没理解啊没理解fopen打开文件了,说明文件是在服务器端了莫非是服务器将文件分成几份,往别的服务器发送?
      

  3.   

    噢,我理会错了你的意思。
    我说的是接收方的做法,其实你问的是发送方的做法发送方以 POST 方式发送数据即可
    数据应至少包含
    offset 本次的数据相对原始文件的偏移
    data 从原始文件的 offset 偏移开始读取的数据块接收方从 $_POST 中得到数据写入目标文件,前面我已经说了
    如果因发送方的原因,无法从 $_POST 获取数据,可从 php://input 流中读取,自行析出
      

  4.   


    fopen返回的是一个resource对象(文件句柄),是否能将这个resource对象拆分成十个resource(文件句柄)对象呢?貌似这样不行,只能通过分块读取字符,写入临时文件,再分别读取这些临时文件并发送
      

  5.   

    本帖最后由 xuzuning 于 2012-04-08 08:16:16 编辑
      

  6.   

    print_r($_POST);//应答$type = file_exists($_POST['name']) ? 'rb+' : 'wb';
    $fp = fopen($_POST['name'], $type);
    fseek($fp, $_POST['offset']);
    fwrite($fp, base64_decode($_POST['data']));
    fclose($fp);
    这段代码在服务器端合并了文件了吗?
      

  7.   

    刚才试了下是可以的,就是说curl_client发送字符流到curl_server进行处理,每一个请求都会根据偏移位置来进行写操作,想问下,这些操作是串行的吗?还是说并行?
      

  8.   

    curl_multi_init 按手册上的说明是并发的如何验证要想想
      

  9.   

    这样可以测试
    服务器端加一句
    $_POST['time'] = microtime();返回的数据中Array
    (
        [id] => 0
        [time] => 0.54687700 1333959939
    )
    Array
    (
        [id] => 1
        [time] => 0.54687700 1333959939
    )
    Array
    (
        [id] => 2
        [time] => 0.53125200 1333959939
    )
    Array
    (
        [id] => 3
        [time] => 0.54687700 1333959939
    )
    Array
    (
        [id] => 4
        [time] => 0.54687700 1333959939
    )
    Array
    (
        [id] => 5
        [time] => 0.54687700 1333959939
    )
    应该可以说明问题了
      

  10.   

    大概原理是对了,准备通过CURL设置头信息来模拟提交数据
      

  11.   

    server1将分块切割后存储到memcache 然后server2服务器端再拼装相关的数据 是否可行?
    小文件我猜估计可以 哈哈
      

  12.   

    @xuzuning有CURL模拟Header头信息上传文件的参考资料吗?
      

  13.   

    我不是很清楚你要干什么,也不知道你做到了哪一步
    我把我的思路给出,供你参考客户端(将你的头像分块传递到服务器端)
    PHP code
    $fn = 'http://avatar.csdn.net/D/5/A/2_skyaspnet.jpg';
    $size = 512;$mch = curl_multi_init ();
    $conn = array ();
    $url = 'http://localhost/curl_server.php';$fp = fopen($fn, 'rb');
    $i = 0;
    while(! feof($fp)) {
      $post = array(
        'id' => $i,
        'name' => basename($fn),
        'offset' => ftell($fp),
        'data' => base64_encode(fread($fp, $size)),
      );
      $conn[$i] = curl_init ( $url );
      curl_setopt( $conn[$i], CURLOPT_POST, true );
      curl_setopt( $conn[$i], CURLOPT_POSTFIELDS, $post );
      curl_setopt ( $conn[$i], CURLOPT_RETURNTRANSFER, true );
      curl_multi_add_handle ( $mch, $conn[$i] );
      $i++;
    }$running = NULL;
    do {
        curl_multi_exec ( $mch, $running );
    } while ( $running > 0 );foreach ( $conn as $i => $v ) {
      print_r(curl_multi_getcontent($v));
      curl_close ( $conn[$i] );
    }curl_multi_close ( $mch );服务器端(将收到的数据写入文件)
    curl_server.php
    PHP code
    print_r($_POST);//应答$type = file_exists($_POST['name']) ? 'rb+' : 'wb';
    $fp = fopen($_POST['name'], $type);
    fseek($fp, $_POST['offset']);
    fwrite($fp, base64_decode($_POST['data']));
    fclose($fp);
    这样可以测试
    服务器端加一句
    $_POST['time'] = microtime();返回的数据中Array
    (
      [id] => 0
      [time] => 0.54687700 1333959939
    )
    Array
    (
      [id] => 1
      [time] => 0.54687700 1333959939
    )
    Array
    (
      [id] => 2
      [time] => 0.53125200 1333959939
    )
    Array
    (
      [id] => 3
      [time] => 0.54687700 1333959939
    )
    Array
    (
      [id] => 4
      [time] => 0.54687700 1333959939
    )
    Array
    (
      [id] => 5
      [time] => 0.54687700 1333959939
    )
    应该可以说明问题了
      

  14.   

    大家回复帖子的时候 最好能使用 CODE!!!
      

  15.   

    大家回复帖子的时候 最好能使用 CODE!!!
      

  16.   

    大家回复帖子的时候 最好能使用 CODE!!!
      

  17.   

    那个swfupload控件就是用base64这种方式处理的, 不过个人建议,有条件的, 还是用 php://input 这种方式, 毕竟解码是很占资源的
      

  18.   

    超越QQ Mail文件中转站---大文件上传设计思路和实践(一) 王泽宾 
           笔者近来一直研究邮件系统,其中感觉QQ邮箱的大附件功能(也叫文件中转站)非常不错,腾讯也自称是同类产品中用户体验最好。
        马化腾先生在内部工作会议上对此也极为称道。本人很感兴趣,近期对其进行了研究,目前已经完成开发并上线开始试用。
        本人完成的系统界面如下,用html、css、js模拟上传效果:
         
        
     
        腾讯qqmail大附件上传分为两种方式:
        第一种通过标准的html中<input type="file">控件上传。
        第二种通过activex控件上传。 
        为什么不完全使用activex控件呢?
        答案是:某些浏览器不支持activex,比如firefox,只能按照标准组件上传(当然也可以开发插件,但与IE的机制完全不同)。另外,IE的安全级别提高后也可能屏蔽activx。
        
        通过activex控件上传有什么好处呢?
        答案是:activex可以实现断点续传,而且可以实时显示上传进度,用户体验好。
        activex实现断点续传的原理也非常简单:先通过activex(可以操作本地文件,突破安全性)将大文件进行按照一定的尺寸切片,然后逐片上传,最后由服务器对文件片拼接为完整的文件。
         笔者通过sniffer抓包程序,跟踪qq大附件上传的过程,结果如下:
       QQ mail先将文件进行扫描,生成摘要md5(为了保证文件的完整性),然后按照每片128K大小,模拟标准的Html的Post方式上传数据,每一份数据的开头都放置了有关文件的基本信息和断点信息。
       sniffer抓到的Post数据头如下:
         POST /ftn_handler HTTP/1.1
         Accept: */*
         User-Agent: TXFTNActiveX
         Host: 124.89.102.10
         Content-Length: 131428
         Connection: Keep-Alive
         Cache-Control: no-cache
        将User-Agent头数据改为自己的标识,其它数据都还算标准。
       QQ mail不使用activex控件上传文件的方式:是使用了标准的webserver的功能,最大支持15M。为什么设定这个尺寸的,感觉有两种可能性:
        第一种:鼓励用户使用 activex方式。
        第二种:功力不够,或者开发者有偷懒的嫌疑,这一点大家不要迷信,其实他们也跟咱没什么两样,程序员只要能交活就行了,没必要加工作量,
    其实从马虎腾的工作报告中也能感受到这一点,他也挺看不过去的。当然老板是老板的心态,公司毕竟是人家自己的。
         笔者也做了两种方式,其中不通过activex控件上传文件的方式最大支持1G,给qq mail补上。activex控件的方式跟qqmail类似,在后续的文章中详述。
      下一篇介绍 非activex方式的原理和实现,请各位关注。  
      

  19.   

    这个功能对于企业来讲非常实用,随着互联网的发展,现在越来越多的信息需要我们处理,同时信息的尺寸也变的越来越大,比如一张高清图片可能就大几十MB,一部高清电影就得奔十几G了,而传统的HTML方式又不支持这种断点续传功能,所以这种需求就越显得突出了。
      

  20.   

    像115网盘,QQ邮箱中的大附件上传控件,DBank(华为网盘),金山快盘等越来越多的互联网企业使用断点续传技术来提高他们的用户体验。
    此控件支持2G文件的断点续传操作,提供了完善的开发文档,支持文件MD5验证,支持文件批量上传。
    文件MD5值计算进度:文件MD5值计算完毕服务器根据MD5检测是否存在相同文件续传文件从服务器加载文件列表文件上传中文件上传完毕上传文件夹与Discuz!X2整合-后台安装断点续传控件与Discuz!X2整合-后台启用断点续传控件与Discuz!X2整合-后台断点续传控件启用成功与Discuz!X2整合-前台发帖页面与Discuz!X2整合-上传
      

  21.   

    // SkinButton.cpp : implementation file
    //#include "stdafx.h"
    #include "CustomSkin.h"
    #include "XButton.h"
    #include <mmsystem.h>
    // CSkinButtonIMPLEMENT_DYNAMIC(CSkinButton, CButton)CSkinButton::CSkinButton()
    {
    m_NormalImags.m_Image.Clear();
    m_OverImages.m_Image.Clear();
    m_DownImages.m_Image.Clear();
    m_DisableImages.m_Image.Clear();
    m_allImage.m_Image.Clear();
    m_CheckedDisableImages.m_Image.Clear(); m_Style = NORMAL;                //按钮形状风格
    b_InRect = false;           //鼠标进入标志
    m_strText = "";         //按钮文字(使用默认文字)
    m_TextForeColor = RGB(0,0,0);             //文字颜色(黑色)
    m_BackColor = RGB(243,243,243);       //背景色(灰白色)
    m_LockForeColor = GetSysColor(COLOR_GRAYTEXT);     //锁定按钮的文字颜色
    p_Font = NULL;  //字体指针
    m_ImageSize.SetSize(0,0); nTxtLeft = 0;
    nTxtTop = 0; nImageLeft = 0;
    nImageTop = 0; //不画焦点虚线框
    m_bDrawFocus = FALSE;
    //不显示tooltip
    bShowTooltip = FALSE;
    }CSkinButton::~CSkinButton()
    {
    if ( p_Font )     
    delete p_Font;         //删除字体
    }BEGIN_MESSAGE_MAP(CSkinButton, CButton)
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONUP()
    ON_WM_MOUSEMOVE()
    ON_WM_SIZE()
    END_MESSAGE_MAP()// CSkinButton message handlersvoid CSkinButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
    CDC *pDC = CDC::FromHandle( lpDrawItemStruct->hDC );
    m_ButRect = lpDrawItemStruct->rcItem;     //获取按钮尺寸 int nSavedDC = pDC->SaveDC();
    VERIFY( pDC );
    //DrawButton( pDC );                 //绘制按钮
    DrawSkinButton(pDC);
    pDC->RestoreDC( nSavedDC ); OutputDebugStr("draw skin button......\r\n");
    }