我通过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");
}
答案 0 :(得分:0)
好吧,看来@NominalAnimal说的是正确的。您可以将二进制数据存储在字符串中,但是当您在string.h
库中使用任何函数时,它几乎总是会更改该字符串中存储的内容(如果数据是二进制的)。
简单的解决方案是创建一个单独的函数,该函数接收指向二进制数据的指针并在该函数中执行字符串搜索,返回所需的相关信息。这样,原始数据永远不会改变。
答案 1 :(得分:-1)
'stdin'是STDIN_FILENO的宏,它是0的平均值。另见'unistd.h'。 你没有显示你的代码,但我认为你遇到'\ 0'或非ascii字符时会停止,因为你说你使用的是'fread()'。
当fread()函数返回0时你必须停止,这意味着它停止阅读:它遇到了EOF。