【问题标题】:File Download via API using Guzzle使用 Guzzle 通过 API 下载文件
【发布时间】:2022-02-08 13:34:59
【问题描述】:

我仍在学习 Guzzle,但我被这个问题难住了......

我正在尝试使用 Guzzle 从第 3 方 API 检索音频文件。我编写了一个包含许多函数的类,除了“下载”函数的最后阶段之外,它们都可以工作 - 浏览器不会提示保存音频文件

调用以获取音频文件,使用 AJAX

$callRows.on("click", ".download", function() {

    const $this = $(this);
    $this.parent().hide().next().show();

    $.ajax({
        type : "POST",
        url  : "submgmt",
        data : {
            task   : "getMedia",
            target : $this.data("id")
        },
        success  : function(data) {

            $this.parent().show().next().hide();

        },
        error : function (XMLHttpRequest, textStatus, errorThrown) {
            $dialog.attr("title", textStatus);
            $dialog.html("<br>" + errorThrown + "<br><br><h4>Click OK to continue</h4>");
            $dialog.dialog({
                buttons  : {
                    "OK" : function () {
                        $dialog.dialog("destroy");
                    }
                },
                dialogClass : "no-close",
                modal       : true,
                resizable   : false
            });
            $this.parent().show().next().hide();
        }
    });
});

第一次尝试 调用下载函数的函数

$ucCall = new ucCalls($ucAuth);

try {

    $response = $ucCall->getMedia($target);

    if ($response['code']===404) {
        $result['ERROR'] = "1";
        $result['DETAILS'] = "File is no longer available";
        echo json_encode($result);
        exit();
    } else {
        header("Date: " . $response['headers']['Date'][0]);
        header("Cache-Control: " . $response['headers']['Cache-Control'][0]);
        header("Pragma: " . $response['headers']['Pragma'][0]);
        header("Expires: " . $response['headers']['Expires'][0]);
        header("Content-Type: " . $response['headers']['Content-Type'][0]);
        header('Content-Disposition: attachment; filename="' . basename($response['filename']) . '"');
        header("Content-Length: " . filesize($response['filename']));
        ob_clean();
        flush();
        readfile($response['filename']);
        exit;
    }
} catch (Exception $e) {
    $result['ERROR']   = "1";
    $result['DETAILS'] = $e->getCode() . " - " . $e->getMessage();
    echo json_encode($result);
    exit();
}

从 3rd 方 API 检索音频文件的类

class ucCalls {

private Client $client;

public function __construct($authString)
{
    $this->client = new Client([
        'base_uri' => 'https://url-of-host',
        'timeout'  => 5,
        'headers' => [
            'Authorization' => 'Basic ' . $authString,
            'Accept' => 'application/json'
        ],
    ]);
}

/**
 * Retrieves the unencrypted MP3 media for a (completed) recording
 * @param string $id
 * @return array
 * @throws Exception
 */
public function getMedia(string $id): array
{

    try {

        $temp_file = sys_get_temp_dir() . "\uc" . make_id(6) . ".mp3";
        $resource = Utils::tryFopen($temp_file, 'w');

        $response = $this->client->request('GET','fw/path/to/audio/file/store/' . $id . '/Media', ['sink' => $resource]);

        return [
            'code'     => $response->getStatusCode(),
            'headers'  => $response->getHeaders(),
            'filename' => $temp_file
        ];

    } catch (ConnectException $e) {
        error_log(print_r("## EXCEPTION ## ucCalls (getMedia) - ConnectException -> " . $e->getMessage(),true));
        throw new Exception ("Connection Error",500);
    } catch (ServerException|ClientException $e) {
        error_log(print_r("## EXCEPTION ## ucCalls (getMedia) - ServerException|ClientException -> " . $e->getMessage(),true));
        throw new Exception ($e->getResponse()->getReasonPhrase(),$e->getResponse()->getStatusCode());
    } catch (GuzzleException $e) {
        error_log(print_r("## EXCEPTION ## ucCalls (getMedia) - GuzzleException -> " . $e->getMessage(),true));
        throw new Exception ("Unhandled Exception",400);
    }

}

}

第二次尝试是尝试使用 Guzzle Stream

调用下载函数的函数

$ucCall = new ucCalls($ucAuth);

try {
    
    $ucCall->getMedia($target);

} catch (Exception $e) {
    $result['ERROR']   = "1";
    $result['DETAILS'] = $e->getCode() . " - " . $e->getMessage();
    echo json_encode($result);
    exit();
}

public function getMedia(string $id)
{

    try {

        $temp_file = sys_get_temp_dir() . "\uc" . make_id(6) . ".mp3";
        $resource = Utils::tryFopen($temp_file, 'w+');
        $stream = Utils::streamFor($resource);

        $this->client->request('GET','fw/path/to/audio/file/store/' . $id . '/Media', ['sink' => $stream]);

        header("Cache-Control: no-cache");
        header("Content-Type: audio/mp3");
        header('Content-Disposition: attachment; filename="' . basename($stream->getMetadata('uri')) . '"');
        header('Content-Length: ' . filesize($stream->getMetadata('uri')));

        echo $stream;

    } catch (ConnectException $e) {
        error_log(print_r("## EXCEPTION ## ucCalls (getMedia) - ConnectException -> " . $e->getMessage(),true));
        throw new Exception ("Connection Error",500);
    } catch (ServerException|ClientException $e) {
        error_log(print_r("## EXCEPTION ## ucCalls (getMedia) - ServerException|ClientException -> " . $e->getMessage(),true));
        throw new Exception ($e->getResponse()->getReasonPhrase(),$e->getResponse()->getStatusCode());
    } catch (GuzzleException $e) {
        error_log(print_r("## EXCEPTION ## ucCalls (getMedia) - GuzzleException -> " . $e->getMessage(),true));
        throw new Exception ("Unhandled Exception",400);
    }
}

这两个函数都在我的本地 Web 服务器上的临时存储中创建音频文件,因此可以从第 3 方检索音频文件。这就是我被难住的地方......

文件似乎正在传输到客户端,标题属性被识别并且正确的数据量被传输(文件大小) - 但客户端浏览器(在 Edge 和 Firefox 中测试)没有提示保存文件。只是得到一个编码的字符串,根据截图...

任何想法或指导将不胜感激......

【问题讨论】:

  • 当您向浏览器显示媒体时,使用正确的标题,例如 Content-type: application/octet-streamContent-Disposition: attachment; filename=audio.mp3,默认情况下它以 text/html 形式发送
  • 谢谢@bhucho,尝试过并尝试了几种内容类型,包括audio/mpegaudio/mp3application/octet-stream - 都给出了相同的响应...
  • 那么有一种方法虽然你应该已经获得了音频文件(我稍后会尝试),尝试this 回答只是通过音频/mpeg 更改音频格式并将其传递到音频标签中,你也可以从这里检查你的音频编码base64.guru/converter/encode/audio
  • js中也有类似的方法
  • @bhucho,这实际上是一个很大的帮助谢谢...我可以看到从网络服务器到客户端浏览器的数据负载已经是 base64 编码...所以我要添加一个媒体播放器到页面以允许用户收听音频文件。然后,“下载”选项可以只是到域文件服务器上的共享文件夹的 FTP。这样会更好,因为这意味着我们不必担心客户端计算机上的音频文件...

标签: php iis guzzle


【解决方案1】:

一旦你得到编码的响应,你可以检查它是否是base64编码或使用php提供的base64_encode($content)

$encoded_response = 'your encoded string';
$base64 = base64_encode($encoded_response);
$mime = "audio/mpeg";
$img = ('data:' . $mime . ';base64,' . $base64); // would be like data:audio/mpeg;base64,YxAAEaAIEeUAQAgBgNg...
<audio autoplay controls src="data:audio/mpeg;base64,YxAAEaAIEeUAQAgBgNg...">
  The “audio” tag is not supported by your browser.
</audio>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-25
    • 1970-01-01
    • 2016-10-18
    • 1970-01-01
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多