【问题标题】:Can fopen be used to open the URL可以用fopen打开网址吗
【发布时间】:2014-10-30 09:06:40
【问题描述】:

fopen("tftp://1.1.1.1/file.txt","rb"); 是一个有效的声明吗? C编程中可以使用fopen打开url吗?

【问题讨论】:

    标签: c linux file-io fopen


    【解决方案1】:

    不,但你可以使用libcurl,例如:

    #include <stdio.h>
    #include <curl/curl.h>
    
    /*
     * This is an example showing how to get a single file from an FTP server.
     * It delays the actual destination file creation until the first write
     * callback so that it won't create an empty file in case the remote file
     * doesn't exist or something else fails.
     */ 
    
    struct FtpFile {
      const char *filename;
      FILE *stream;
    };
    
    static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
    {
      struct FtpFile *out=(struct FtpFile *)stream;
      if(out && !out->stream) {
        /* open file for writing */ 
        out->stream=fopen(out->filename, "wb");
        if(!out->stream)
          return -1; /* failure, can't open file to write */ 
      }
      return fwrite(buffer, size, nmemb, out->stream);
    }
    
    
    int main(void)
    {
      CURL *curl;
      CURLcode res;
      struct FtpFile ftpfile={
        "curl.tar.gz", /* name to store the file as if succesful */ 
        NULL
      };
    
      curl_global_init(CURL_GLOBAL_DEFAULT);
    
      curl = curl_easy_init();
      if(curl) {
        /*
         * You better replace the URL with one that works!
         */ 
        curl_easy_setopt(curl, CURLOPT_URL,
                         "ftp://ftp.example.com/pub/www/utilities/curl/curl-7.9.2.tar.gz");
        /* Define our callback to get called when there's data to be written */ 
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
        /* Set a pointer to our struct to pass to the callback */ 
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);
    
        /* Switch on full protocol/debug output */ 
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
    
        res = curl_easy_perform(curl);
    
        /* always cleanup */ 
        curl_easy_cleanup(curl);
    
        if(CURLE_OK != res) {
          /* we failed */ 
          fprintf(stderr, "curl told us %d\n", res);
        }
      }
    
      if(ftpfile.stream)
        fclose(ftpfile.stream); /* close the local file */ 
    
      curl_global_cleanup();
    
      return 0;
    }
    

    或者(正如@Paul 所指出的)你可以使用popen 来管道一个进程(例如:wget url):

    #include <stdio.h>
    
    FILE *popen(const char *command, const char *mode);
    int pclose(FILE *stream);
    
    int main(void)
    {
        /* wget -q = silent mode */
        FILE *cmd = popen("wget -q -O - ftp://debian.org/debian-security/README.security", "r");
        char result[1024];
    
        while (fgets(result, sizeof(result), cmd) != NULL)
            printf("%s", result);
        pclose(cmd);
        return 0;
    }
    

    【讨论】:

      【解决方案2】:

      &lt;stdio.h&gt; 中的 fopen 不会这样做。

      但是,没有什么能阻止某人编写一个名为 fopen() 的函数来执行其他操作。

      FILE *popen(const char *command, const char *mode) 可用于生成一个运行适当命令行工具(例如tftpwget)的进程,从而将远程资源下载到可从C 代码访问的文件描述符中。 popen() 调用的语法与您所展示的非常相似。但是,它缺少下载实用程序的程序名称。裸 url 或 ftp 地址不适用于 popen()

      见:

      另请注意:

      PHP language version of fopen()确实打开裸 URL。但是 PHP != C

      【讨论】:

      • +1,简单便携!,OP,您对我的答案进行了编辑,显示了一个示例。
      【解决方案3】:

      没有fopen那么简单,但可以做到。

      您需要使用 libcurl。看看here

      来自网站:

      /*****************************************************************************
       *
       * This example requires libcurl 7.9.7 or later.
       */ 
      
      #include <stdio.h>
      #include <string.h>
      #ifndef WIN32
      #include <sys/time.h>
      #endif
      #include <stdlib.h>
      #include <errno.h>
      
      #include <curl/curl.h>
      
      enum fcurl_type_e {
        CFTYPE_NONE=0,
        CFTYPE_FILE=1,
        CFTYPE_CURL=2
      };
      
      struct fcurl_data
      {
        enum fcurl_type_e type;     /* type of handle */ 
        union {
          CURL *curl;
          FILE *file;
        } handle;                   /* handle */ 
      
        char *buffer;               /* buffer to store cached data*/ 
        size_t buffer_len;          /* currently allocated buffers length */ 
        size_t buffer_pos;          /* end of data in buffer*/ 
        int still_running;          /* Is background url fetch still in progress */ 
      };
      
      typedef struct fcurl_data URL_FILE;
      
      /* exported functions */ 
      URL_FILE *url_fopen(const char *url,const char *operation);
      int url_fclose(URL_FILE *file);
      int url_feof(URL_FILE *file);
      size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
      char * url_fgets(char *ptr, size_t size, URL_FILE *file);
      void url_rewind(URL_FILE *file);
      
      /* we use a global one for convenience */ 
      CURLM *multi_handle;
      
      /* curl calls this routine to get more data */ 
      static size_t write_callback(char *buffer,
                                   size_t size,
                                   size_t nitems,
                                   void *userp)
      {
        char *newbuff;
        size_t rembuff;
      
        URL_FILE *url = (URL_FILE *)userp;
        size *= nitems;
      
        rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */ 
      
        if(size > rembuff) {
          /* not enough space in buffer */ 
          newbuff=realloc(url->buffer,url->buffer_len + (size - rembuff));
          if(newbuff==NULL) {
            fprintf(stderr,"callback buffer grow failed\n");
            size=rembuff;
          }
          else {
            /* realloc suceeded increase buffer size*/ 
            url->buffer_len+=size - rembuff;
            url->buffer=newbuff;
          }
        }
      
        memcpy(&url->buffer[url->buffer_pos], buffer, size);
        url->buffer_pos += size;
      
        return size;
      }
      
      /* use to attempt to fill the read buffer up to requested number of bytes */ 
      static int fill_buffer(URL_FILE *file, size_t want)
      {
        fd_set fdread;
        fd_set fdwrite;
        fd_set fdexcep;
        struct timeval timeout;
        int rc;
      
        /* only attempt to fill buffer if transactions still running and buffer
         * doesnt exceed required size already
         */ 
        if((!file->still_running) || (file->buffer_pos > want))
          return 0;
      
        /* attempt to fill buffer */ 
        do {
          int maxfd = -1;
          long curl_timeo = -1;
      
          FD_ZERO(&fdread);
          FD_ZERO(&fdwrite);
          FD_ZERO(&fdexcep);
      
          /* set a suitable timeout to fail on */ 
          timeout.tv_sec = 60; /* 1 minute */ 
          timeout.tv_usec = 0;
      
          curl_multi_timeout(multi_handle, &curl_timeo);
          if(curl_timeo >= 0) {
            timeout.tv_sec = curl_timeo / 1000;
            if(timeout.tv_sec > 1)
              timeout.tv_sec = 1;
            else
              timeout.tv_usec = (curl_timeo % 1000) * 1000;
          }
      
          /* get file descriptors from the transfers */ 
          curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
      
          /* In a real-world program you OF COURSE check the return code of the
             function calls.  On success, the value of maxfd is guaranteed to be
             greater or equal than -1.  We call select(maxfd + 1, ...), specially
             in case of (maxfd == -1), we call select(0, ...), which is basically
             equal to sleep. */ 
      
          rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
      
          switch(rc) {
          case -1:
            /* select error */ 
            break;
      
          case 0:
          default:
            /* timeout or readable/writable sockets */ 
            curl_multi_perform(multi_handle, &file->still_running);
            break;
          }
        } while(file->still_running && (file->buffer_pos < want));
        return 1;
      }
      
      /* use to remove want bytes from the front of a files buffer */ 
      static int use_buffer(URL_FILE *file,int want)
      {
        /* sort out buffer */ 
        if((file->buffer_pos - want) <=0) {
          /* ditch buffer - write will recreate */ 
          if(file->buffer)
            free(file->buffer);
      
          file->buffer=NULL;
          file->buffer_pos=0;
          file->buffer_len=0;
        }
        else {
          /* move rest down make it available for later */ 
          memmove(file->buffer,
                  &file->buffer[want],
                  (file->buffer_pos - want));
      
          file->buffer_pos -= want;
        }
        return 0;
      }
      
      URL_FILE *url_fopen(const char *url,const char *operation)
      {
        /* this code could check for URLs or types in the 'url' and
           basicly use the real fopen() for standard files */ 
      
        URL_FILE *file;
        (void)operation;
      
        file = malloc(sizeof(URL_FILE));
        if(!file)
          return NULL;
      
        memset(file, 0, sizeof(URL_FILE));
      
        if((file->handle.file=fopen(url,operation)))
          file->type = CFTYPE_FILE; /* marked as URL */ 
      
        else {
          file->type = CFTYPE_CURL; /* marked as URL */ 
          file->handle.curl = curl_easy_init();
      
          curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
          curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
          curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 0L);
          curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);
      
          if(!multi_handle)
            multi_handle = curl_multi_init();
      
          curl_multi_add_handle(multi_handle, file->handle.curl);
      
          /* lets start the fetch */ 
          curl_multi_perform(multi_handle, &file->still_running);
      
          if((file->buffer_pos == 0) && (!file->still_running)) {
            /* if still_running is 0 now, we should return NULL */ 
      
            /* make sure the easy handle is not in the multi handle anymore */ 
            curl_multi_remove_handle(multi_handle, file->handle.curl);
      
            /* cleanup */ 
            curl_easy_cleanup(file->handle.curl);
      
            free(file);
      
            file = NULL;
          }
        }
        return file;
      }
      
      int url_fclose(URL_FILE *file)
      {
        int ret=0;/* default is good return */ 
      
        switch(file->type) {
        case CFTYPE_FILE:
          ret=fclose(file->handle.file); /* passthrough */ 
          break;
      
        case CFTYPE_CURL:
          /* make sure the easy handle is not in the multi handle anymore */ 
          curl_multi_remove_handle(multi_handle, file->handle.curl);
      
          /* cleanup */ 
          curl_easy_cleanup(file->handle.curl);
          break;
      
        default: /* unknown or supported type - oh dear */ 
          ret=EOF;
          errno=EBADF;
          break;
        }
      
        if(file->buffer)
          free(file->buffer);/* free any allocated buffer space */ 
      
        free(file);
      
        return ret;
      }
      
      int url_feof(URL_FILE *file)
      {
        int ret=0;
      
        switch(file->type) {
        case CFTYPE_FILE:
          ret=feof(file->handle.file);
          break;
      
        case CFTYPE_CURL:
          if((file->buffer_pos == 0) && (!file->still_running))
            ret = 1;
          break;
      
        default: /* unknown or supported type - oh dear */ 
          ret=-1;
          errno=EBADF;
          break;
        }
        return ret;
      }
      
      size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
      {
        size_t want;
      
        switch(file->type) {
        case CFTYPE_FILE:
          want=fread(ptr,size,nmemb,file->handle.file);
          break;
      
        case CFTYPE_CURL:
          want = nmemb * size;
      
          fill_buffer(file,want);
      
          /* check if theres data in the buffer - if not fill_buffer()
           * either errored or EOF */ 
          if(!file->buffer_pos)
            return 0;
      
          /* ensure only available data is considered */ 
          if(file->buffer_pos < want)
            want = file->buffer_pos;
      
          /* xfer data to caller */ 
          memcpy(ptr, file->buffer, want);
      
          use_buffer(file,want);
      
          want = want / size;     /* number of items */ 
          break;
      
        default: /* unknown or supported type - oh dear */ 
          want=0;
          errno=EBADF;
          break;
      
        }
        return want;
      }
      
      char *url_fgets(char *ptr, size_t size, URL_FILE *file)
      {
        size_t want = size - 1;/* always need to leave room for zero termination */ 
        size_t loop;
      
        switch(file->type) {
        case CFTYPE_FILE:
          ptr = fgets(ptr,size,file->handle.file);
          break;
      
        case CFTYPE_CURL:
          fill_buffer(file,want);
      
          /* check if theres data in the buffer - if not fill either errored or
           * EOF */ 
          if(!file->buffer_pos)
            return NULL;
      
          /* ensure only available data is considered */ 
          if(file->buffer_pos < want)
            want = file->buffer_pos;
      
          /*buffer contains data */ 
          /* look for newline or eof */ 
          for(loop=0;loop < want;loop++) {
            if(file->buffer[loop] == '\n') {
              want=loop+1;/* include newline */ 
              break;
            }
          }
      
          /* xfer data to caller */ 
          memcpy(ptr, file->buffer, want);
          ptr[want]=0;/* allways null terminate */ 
      
          use_buffer(file,want);
      
          break;
      
        default: /* unknown or supported type - oh dear */ 
          ptr=NULL;
          errno=EBADF;
          break;
        }
      
        return ptr;/*success */ 
      }
      
      void url_rewind(URL_FILE *file)
      {
        switch(file->type) {
        case CFTYPE_FILE:
          rewind(file->handle.file); /* passthrough */ 
          break;
      
        case CFTYPE_CURL:
          /* halt transaction */ 
          curl_multi_remove_handle(multi_handle, file->handle.curl);
      
          /* restart */ 
          curl_multi_add_handle(multi_handle, file->handle.curl);
      
          /* ditch buffer - write will recreate - resets stream pos*/ 
          if(file->buffer)
            free(file->buffer);
      
          file->buffer=NULL;
          file->buffer_pos=0;
          file->buffer_len=0;
      
          break;
      
        default: /* unknown or supported type - oh dear */ 
          break;
        }
      }
      
      /* Small main program to retrive from a url using fgets and fread saving the
       * output to two test files (note the fgets method will corrupt binary files if
       * they contain 0 chars */ 
      int main(int argc, char *argv[])
      {
        URL_FILE *handle;
        FILE *outf;
      
        int nread;
        char buffer[256];
        const char *url;
      
        if(argc < 2)
          url="http://192.168.7.3/testfile";/* default to testurl */ 
        else
          url=argv[1];/* use passed url */ 
      
        /* copy from url line by line with fgets */ 
        outf=fopen("fgets.test","w+");
        if(!outf) {
          perror("couldn't open fgets output file\n");
          return 1;
        }
      
        handle = url_fopen(url, "r");
        if(!handle) {
          printf("couldn't url_fopen() %s\n", url);
          fclose(outf);
          return 2;
        }
      
        while(!url_feof(handle)) {
          url_fgets(buffer,sizeof(buffer),handle);
          fwrite(buffer,1,strlen(buffer),outf);
        }
      
        url_fclose(handle);
      
        fclose(outf);
      
      
        /* Copy from url with fread */ 
        outf=fopen("fread.test","w+");
        if(!outf) {
          perror("couldn't open fread output file\n");
          return 1;
        }
      
        handle = url_fopen("testfile", "r");
        if(!handle) {
          printf("couldn't url_fopen() testfile\n");
          fclose(outf);
          return 2;
        }
      
        do {
          nread = url_fread(buffer, 1,sizeof(buffer), handle);
          fwrite(buffer,1,nread,outf);
        } while(nread);
      
        url_fclose(handle);
      
        fclose(outf);
      
      
        /* Test rewind */ 
        outf=fopen("rewind.test","w+");
        if(!outf) {
          perror("couldn't open fread output file\n");
          return 1;
        }
      
        handle = url_fopen("testfile", "r");
        if(!handle) {
          printf("couldn't url_fopen() testfile\n");
          fclose(outf);
          return 2;
        }
      
        nread = url_fread(buffer, 1,sizeof(buffer), handle);
        fwrite(buffer,1,nread,outf);
        url_rewind(handle);
      
        buffer[0]='\n';
        fwrite(buffer,1,1,outf);
      
        nread = url_fread(buffer, 1,sizeof(buffer), handle);
        fwrite(buffer,1,nread,outf);
      
      
        url_fclose(handle);
      
        fclose(outf);
      
      
        return 0;/* all done */ 
      }
      

      【讨论】:

        猜你喜欢
        • 2012-04-09
        • 1970-01-01
        • 1970-01-01
        • 2012-05-06
        • 2011-09-13
        • 2015-07-20
        • 1970-01-01
        • 2011-02-21
        • 2010-12-12
        相关资源
        最近更新 更多