如何将STDIN流更改为二进制

时间:2017-04-15 16:46:14

标签: c linux cgi stdin binary-data

我通过CGI界面制作上传表格。我是用C语言写的,不想使用任何外部库(即cgic)。

我认为程序已经完成,因为第一个测试文件已正确上传。但它们是ASCII文件。当我用二进制文件(JPG)测试时。似乎STDIN正在尝试将二进制数据读取为ASCII,这会对存在于ASCII文件末尾的\0等字符造成问题,但这是二进制文件中的常见字符。上传1.9MB文件的结果最终为38kB文件。

当搜索如何将STDIN流更改为二进制时,我被引用命令freopen并被告知使用NULL作为文件的参数。 example 1

它说:

  

如果filename是空指针,则freopen()函数应尝试   将流的模式更改为mode指定的模式,就好像是   已使用当前与流关联的文件的名称。   在这种情况下,与流关联的文件描述符不需要   如果对freopen()的调用成功,则关闭。它是   实现 - 定义允许的模式更改(如果有),   在什么情况下。

但是当我使用man 3 freopen检查系统上的手册页时,它并没有说任何一个 这一点。此外,阅读手册页,我发现了 二进制选项(向模式添加' b')不再被识别 仅存在于古老的合规性中:

  

模式字符串也可以包括   这封信' b'无论是作为最后一个角色还是作为一个角色   上述任意两个字符串中的字符。   这完全是为了与C89兼容而没有效果; ''   在所有符合POSIX标准的系统上都被忽略,包括Linux。

所以现在我完全迷失了。如何更改STDIN流以读取二进制输入?

以下是代码:

#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

// Declare constants.
#define BUF_SIZE                 4096
#define FILENAME_SIZE             500
#define MARKER_SIZE               100
#define RETURN_FAILURE              0
#define RETURN_SUCCESS              1
#define SEARCH_STRING_1             "filename=\""
#define SEARCH_STRING_2             "\r\n\r\n"

// Declare global variables.
char filename[FILENAME_SIZE + 1];
char *program_name;

// Declare function prototype.
void print_footer (void);
void print_header (void);
void process_input (char *data);

int main (int argc, char *argv[])
{
// Declare variables.
    long long ret;
    char buf[BUF_SIZE + 1];

// Get program name for error reporting.
    program_name = basename(argv[0]);

// Prepare output for browser.
    print_header();

// Protect variable against buffer overflow.
    buf[BUF_SIZE] = '\0';

// Loop through all the file data.
    while(1)
    {
// Read in the next block of data.
        if((ret = (long long) fread(buf, 1, BUF_SIZE, stdin)) != BUF_SIZE)
        {
// Check for error.
            if(ferror(stdin) != 0)
            {
                printf("%s: An error occurred while reading the input file.<br>\n", program_name);
                process_input(NULL);
                exit(EXIT_FAILURE);
            }
// Check for EOF.
            else if(feof(stdin) != 0)
                break;
        }

// Terminate and process uploaded data.
        buf[ret] = '\0';
        process_input(buf);
    }

// Terminate and process uploaded data.
    buf[ret] = '\0';
    process_input(buf);

// Finish user output, close output file and exit.
    print_footer();
    process_input(NULL);
    exit(EXIT_SUCCESS);
}

void process_input (char *data)
{
// Declare variables.
    char *ptr1= NULL;
    char *ptr2;
    int x = 0;
    static FILE *fp;
    static int flag = 0;
    static char marker[MARKER_SIZE + 1];

// If data is NULL, close output file.
    if(data == NULL)
    {
        if(fclose(fp) == EOF)
        {
            printf("%s: process_input: close failed (%s)<br>\n", program_name, strerror(errno));
            exit(EXIT_FAILURE);
        }

        return;
    }

// Check if this is the first time through.
    if(flag == 0)
    {
// Get marker.
        if((ptr1 = strchr(data, '\n')) == NULL)
        {
            printf("%s: process_input: strchr(1) failed (\n)<br>\n", program_name);
            exit(EXIT_FAILURE);
        }

        ptr1[0] = '\0';
        strcpy(marker, data);
        ptr1[0] = '\n';

// Get filename.
        if((ptr1 = strstr(data, SEARCH_STRING_1)) == NULL)
        {
            printf("%s: process_input: strstr(1) failed (%s)<br>\n", program_name, SEARCH_STRING_1);
            exit(EXIT_FAILURE);
        }

// Advance pointer to start of filename.
        ptr1 += 10;

// Find end of filename.
        if((ptr2 = strchr(ptr1, '"')) == NULL)
        {
            printf("%s: process_input: strchr(2) failed (\")<br>\n", program_name);
            exit(EXIT_FAILURE);
        }

// Terminate and store filename.
        ptr2[0] = '\0';
        strcpy(filename, ptr1);
        ptr2[0] = '"';

// Remove spaces from filename.
        while(filename[x] != '\0')
        {
            if(filename[x] == ' ')
                filename[x] = '.';

            x++;
        }

// Open output file.
        if((fp = fopen(filename, "wb")) == NULL)
        {
            printf("%s: process_input: fopen failed (%s) (%s)<br>\n", program_name, strerror(errno), filename);
            exit(EXIT_FAILURE);
        }

// Find start of file data.
        if((ptr1 = strstr(data, SEARCH_STRING_2)) == NULL)
        {
            printf("%s: process_input: strstr(2) failed (%s)<br>\n", program_name, SEARCH_STRING_2);
            fclose(fp);
            exit(EXIT_FAILURE);
        }

// Set flag.
        flag++;
// Advance pointer to start of file data.
        ptr1 += 4;

// Change STDIN stream to binary.
        if(freopen(NULL, "rb", stdin) == NULL)
        {
            printf("%s: process_input: freopen failed (%s)<br>\n", program_name, strerror(errno));
            fclose(fp);
            exit(EXIT_FAILURE);
        }
    }
// Catch everything else.
    else
    {
        ptr1 = data;

        if((ptr2 = strstr(ptr1, marker)) != NULL)
            ptr2[0 - 2] = '\0';
    }

// Write file data.
    if(fwrite(ptr1, 1, strlen(ptr1), fp) != strlen(ptr1))
    {
        printf("%s: process_input: write failed (%s)<br>\n", program_name, strerror(errno));
        fclose(fp);
        exit(EXIT_FAILURE);
    }
}

void print_footer (void)
{
    printf("\nMade it!\n");
}

void print_header (void)
{
    printf("Content-type: text/plain\r\n\r\n");
}

2 个答案:

答案 0 :(得分:0)

好吧,看来@NominalAnimal说的是正确的。您可以将二进制数据存储在字符串中,但是当您在string.h库中使用任何函数时,它几乎总是会更改该字符串中存储的内容(如果数据是二进制的)。

简单的解决方案是创建一个单独的函数,该函数接收指向二进制数据的指针并在该函数中执行字符串搜索,返回所需的相关信息。这样,原始数据永远不会改变。

答案 1 :(得分:-1)

'stdin'是STDIN_FILENO的宏,它是0的平均值。另见'unistd.h'。 你没有显示你的代码,但我认为你遇到'\ 0'或非ascii字符时会停止,因为你说你使用的是'fread()'。

当fread()函数返回0时你必须停止,这意味着它停止阅读:它遇到了EOF。