10

我正在编写一个 php 应用程序,它通过 curl 数据提交以注册 iContact 电子邮件列表。但是,我不断收到无效的电子邮件地址错误。我认为这可能是因为我正在转义 @ 符号,所以它看起来像 %40 而不是 @。此外,根据带有 CURLOPT_POSTFIELDS 的 curl_setopt 的 php 文档:

在 HTTP“POST”操作中发布的完整数据。要发布文件,请在文件名前加上 @ 并使用完整路径。

那么,有没有办法通过 php 中的 curl 将 @ 符号作为发布数据传递,而不先通过 urlencode 运行它?

4

6 回答 6

14

http_build_query()在将它传递给之前先在您的数据数组上使用curl_setopt(),这将导致它发送表单application/x-www-form-encoded而不是multipart/form-data(因此不会解释 @)。

另外,您为什么真正关心电子邮件地址中的@?只有@是第一个字符才重要,而不是中间的某个地方。

于 2009-03-15T18:33:44.333 回答
5

在搜索 PHP curl 手册后,我发现如果 post 字段是字符串而不是文件,如果 post 使用 multipart/form-data 编码,则没有信息可以转义第一个 '@'。

我解决这个问题的方法是在文本的开头添加一个空格。而我们的后端 API 将删除空白,以便它可以删除空白并恢复原始文本。我不知道 Twitter API 是否会在用户输入时修剪空白。

如果是这样,此解决方法也适用于您。

如果有人在使用多部分/表单数据编码的 PHP curl 时找到了转义第一个“@”的方法,请告诉我们。

于 2012-02-04T02:28:03.020 回答
4

我遇到了同样的问题,虽然是 curl 本身而不是 PHP curl。

当使用 curl 的字段选项 ' -F' 时,不会在 POST 中发送前导 @ 符号,而是指示 curl 将紧接在符号之后的文件名作为 POST 的一部分发送。

幸运的是,curl 提供了另一个选项 ' --form-string',其行为方式与 ' -F' 相同,只是不解析 'form-string' 选项。

例如,如果你想使用 curl 来 POST field1 的值为“@value”,file1 的值为“testfile.txt”,你可以这样做:

curl "http://www.url.com" --form-string "field1=@value" -F "file1=@testfile.txt"
于 2011-12-09T02:48:40.520 回答
4

这是可以同时支持包含字符串文件的真正解决方案。@

PHP 5.6 或更高版本的解决方案:

  • 使用CURLFile而不是@.

PHP 5.5 或更高版本的解决方案:

  • 启用CURLOPT_SAFE_UPLOAD.
  • 使用CURLFile而不是@.

PHP 5.3 或更高版本的解决方案:

  • 自己构建多部分内容体。
  • 自己更改Content-Type标题。

以下代码段将为您提供帮助:D

<?php

/**
 * For safe multipart POST request for PHP5.3 ~ PHP 5.4.
 * 
 * @param resource $ch cURL resource
 * @param array $assoc "name => value"
 * @param array $files "name => path"
 * @return bool
 */
function curl_custom_postfields($ch, array $assoc = array(), array $files = array()) {
    
    // invalid characters for "name" and "filename"
    static $disallow = array("\0", "\"", "\r", "\n");
    
    // initialize body
    $body = array();
    
    // build normal parameters
    foreach ($assoc as $k => $v) {
        $k = str_replace($disallow, "_", $k);
        $body[] = implode("\r\n", array(
            "Content-Disposition: form-data; name=\"{$k}\"",
            "",
            filter_var($v), 
        ));
    }
    
    // build file parameters
    foreach ($files as $k => $v) {
        switch (true) {
            case false === $v = realpath(filter_var($v)):
            case !is_file($v):
            case !is_readable($v):
                continue; // or return false, throw new InvalidArgumentException
        }
        $data = file_get_contents($v);
        $v = call_user_func("end", explode(DIRECTORY_SEPARATOR, $v));
        list($k, $v) = str_replace($disallow, "_", array($k, $v));
        $body[] = implode("\r\n", array(
            "Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v}\"",
            "Content-Type: application/octet-stream",
            "",
            $data,
        ));
    }
    
    // generate safe boundary 
    do {
        $boundary = "---------------------" . md5(mt_rand() . microtime());
    } while (preg_grep("/{$boundary}/", $body));
    
    // add boundary for each parameters
    array_walk($body, function (&$part) use ($boundary) {
        $part = "--{$boundary}\r\n{$part}";
    });
    
    // add final boundary
    $body[] = "--{$boundary}--";
    $body[] = "";
    
    // set options
    return curl_setopt_array($ch, array(
        CURLOPT_POST       => true,
        CURLOPT_POSTFIELDS => implode("\r\n", $body),
        CURLOPT_HTTPHEADER => array(
            "Expect: 100-continue",
            "Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
        ),
    ));

}

?>
于 2014-06-05T10:49:03.900 回答
1

@PatricDaryll 的回答是正确的,但我需要做一些研究来了解在哪里使用这个http_build_query函数。

为了澄清和总结,而不是这样做:

     curl_setopt($ch, CURLOPT_POSTFIELDS, $array);

您将使用:

     curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($array));

简单但令人困惑...... curl 足够聪明,可以理解您是否给了他一个字符串或数组。

于 2014-03-20T14:58:01.023 回答
0

为了对非文件数据中的@符号进行转义,您需要执行以下操作。在文本字符串前面加上 NULL 字符

$postfields = array(
    'upload_file' => '@file_to_upload.png',
    'upload_text' => sprintf("\0%s", '@text_to_upload')
);

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://example.com/upload-test');
curl_setopt($curl, CURLOPT_POSTFIELDS, $postfields);
curl_exec($curl);
curl_close($curl);
于 2013-10-11T08:46:12.950 回答