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