最近把一个程序程序部署到Docker启动时一直报 no such file
错误。经排查,发现是程序启动加载本地证书文件时找不到该文件。加载代码如下:
public static X509Certificate2 GetCertificate(IConfiguration configuration)
{
var certFullPath = Path.Combine(Directory.GetCurrentDirectory(), configuration["IdentityServer:Certificates:CerPath"]);
return new X509Certificate2(certFullPath, configuration["IdentityServer:Certificates:Password"]);
}
从配置文件读取相对路径,然后和当前目录combile获取绝对路径。本地运行是没有问题的。
首先猜想是不是 Directory.GetCurrentDirectory()
获取路径的方式在发布后发生了变化,于是改成下列方式获取,
var assembly = typeof(IdentityServerConfig).GetTypeInfo().Assembly;
string currentDirectory = Path.GetDirectoryName(assembly.Location);
var certFullPath = Path.Combine(currentDirectory, configuration["IdentityServer:Certificates:CerPath"]);
结果仍然不成功。于是使用命令 docker cp {container_id}:{src_path} {dest_path}
将容器中的文件复制到本地,并对比了路径也没发现任何问题,更奇怪的时,本地运行该路径下的程序也能正常启动。
消耗N级脑细胞后,怀疑是不是加载file的方式在docker中不一样。因为docker中的file并没有类似磁盘 C://
这样的结构。在参考了一些资料后,采用内嵌资源的方式最终完成了证书的读取。
public static X509Certificate2 GetCertificate(IConfiguration configuration)
{
var assembly = typeof(IdentityServerConfig).GetTypeInfo().Assembly;
//程序集命名空间.资源文件所在文件夹名.资源文件名
using (var stream = assembly.GetManifestResourceStream("gt.IdentityServer.Security.certificate.idsrv4.pfx"))
{
return new X509Certificate2(ReadStream(stream), configuration["IdentityServer:Certificates:Password"]);
}
}
private static byte[] ReadStream(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
这里2个注意点:
- 证书文件需要设置为
内嵌的资源
- GetManifestResourceStream的参数格式为
程序集命名空间.资源文件所在文件夹名.资源文件名