摘要从远程Web服务
我需要检索存储在父应用程序从一个链接的子应用程序的客户端附件返回附件。附件可通过Web服务调用在父应用程序中获得 - 该应用程序返回标准FileContentResult,其内容类型为“application/octet-stream”。我能想到的最好方法是通过WebRequest检索,并将生成的响应流传递给FileStreamResult,尽管我有一些替代方法可用。
有没有人知道是否在制作WebRequest时,一旦响应的第一部分被返回或缓冲后立即响应流变为可用,所以我没有得到响应,直到检索到所有数据?
除了下面完整问题中列出的那些选项以外,还有其他选择吗? (除了将附件保留在儿童和父母的数据库中 - 我真的不想这样做,因为我需要定期同步它们)。
TLDR版本
我有过一个RESTful网络服务,通信的两个相关应用。父应用程序维护可能具有附件的实体的集合。例如,一个请求可能有一个Excel电子表格作为附件。实体及其附件存储在数据库中,使用与访问请求相同的逻辑来控制对附件的访问。也就是说,如果您无法查看请求,则无法下载附件。
在子应用程序中,我为分配给特定机构的实体维护一些集成粘合剂 - 该应用程序用于在我们的董事会和每个董事会学校之间进行通信。我不想维护和同步整个实体/附件。我只想维护足够的信息以允许我连接父应用程序中的Web服务,并获取子应用程序的特定实例有权访问的实体的详细信息。
这适用于实体数据本身。数据量很小,子应用程序中的缓冲开销并不会在访问数据时出现明显的延迟。如有必要,我可以在本地缓存数据以避免性能损失。
我担心的是附件。我已经考虑了三种不同的机制来提供从子应用程序的客户端访问附件。
生成一次性使用令牌和关联的url,允许客户端直接从父应用程序下载附件。令牌生成Web服务调用将确保子应用程序的用户应该有权访问该附件。这样做的缺点是你只能在客户端点击链接一次。再次点击会导致错误,而不是获取附件。
缓冲子应用中的附件。在这种情况下,我会提供一个控制器/操作以将附件下载到子应用程序中,然后调用Web服务方法来获取附件并让子应用程序将附件作为FileContentResult发送。这消除了只能点击链接一次的问题,但附件可能相当大,并且缓冲子应用程序中的数据可能会使下载附件的时间增加一倍,并且更糟糕的是,会在附件下载开始。
链接在子应用程序中,但直接将来自Web服务请求的流提供给FileStreamResult。对我来说,这看起来是FileStreamResult读入块的最佳选择,而不是在将所有数据发送到客户端之前都可用。我在这里可以看到的唯一缺点是我无法直接处理WebResponse,因为直到我的操作返回后才会执行FileStreamResult。
以下是我对为(2)和(3)API包装代码代码:
private class ResponseModel<T> : IDisposable
{
public T Model { get; set; }
public WebResponse Response { get; set; }
private bool Disposed { get; set; }
private void Dispose(bool disposing)
{
if (!Disposed)
{
if (disposing)
{
((IDisposable)this.Response).Dispose();
}
Disposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
private ResponseModel<T> GetAttachmentResponse<T>(long id) where T : IDownloadModel, new()
{
var request = GetRequest(string.Format("{0}/api/getattachment/{1}/{2}", this.BaseUrl, this.Key, id));
var response = request.GetResponse();
var model = (T)Activator.CreateInstance<T>();
var contentDisposition = response.Headers["Content-Disposition"];
if (!string.IsNullOrEmpty(contentDisposition))
{
var filename = contentDisposition.Split(new[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.SingleOrDefault(s => s.StartsWith("filename", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(filename))
{
model.Name = filename.Split('=').Skip(1).FirstOrDefault();
}
}
if (string.IsNullOrEmpty(model.Name))
{
model.Name = "untitled";
}
return new ResponseModel<T> { Model = model, Response = response };
}
public FileDownloadModel GetAttachment(long id)
{
using (var response = GetAttachmentResponse<FileDownloadModel>(id))
{
var reader = new BinaryReader(response.Response.GetResponseStream());
response.Model.Content = reader.ReadBytes((int)response.Response.ContentLength);
return response.Model;
}
}
public FileStreamDownloadModel GetAttachmentStream(long id)
{
// since we're returning the stream, we can't dispose of the response when done.
var response = GetAttachmentResponse<FileStreamDownloadModel>(id);
response.Model.Stream = response.Response.GetResponseStream();
return response.Model;
}
public interface IDownloadModel
{
string ContentType { get; }
string Name { get; set; }
}
模型类
public class FileDownloadModel : IDownloadModel
{
public byte[] Content { get; set; }
public string Name { get; set; }
public string ContentType { get { return "application/octet-stream"; } }
}
public class FileStreamDownloadModel : IDownloadModel
{
public Stream Stream { get; set; }
public string Name { get; set; }
public string ContentType { get { return "application/octet-stream"; } }
}
Bueller? Bueller?我所得到的就是这个蟋蟀。 – tvanfosson
如果两个应用程序都在您的控制之下,您可以允许来自其他域的跨站点脚本,并使用Javascript显示您喜欢的任何附件。请参阅:HttpContext.Current.Response.AddHeader(“Access-Control-Allow-Origin”,“”); –
wilsotc