8

Sorun, WebApi 2 ve bir Async ApiController - tabanlı Get yöntemini kullanırken, bir dosyanın içeriğini döndürmesidir. Get yöntemini eşzamanlı olarak değiştirdiğimde, yalnızca gayet iyi çalışır, ancak onu tekrar uyumsuz duruma döndürdüğümde akışı derhal kapatır. -----C# Async ApiController OutputStream Kapanışı Erken Başlangıçta

public async Task GetDownloadStream(string fileIdentifier, Stream streamToCopyTo) 
{ 
    string filePath = Path.Combine(fileIdentifierFolder, fileIdentifier); 
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, false)) 
    { 
     fs.CopyTo(streamToCopyTo); 
    } 
} 

-------- zaman uyumsuz kod:

public void Get(int id) 
    { 
     try 
     { 
      FileInfo fileInfo = logic.GetFileInfoSync(id); 
      HttpResponse response = HttpContext.Current.Response; 
      response.Clear(); 
      response.ClearContent(); 
      response.Buffer = true; 
      response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.Node.Name + fileInfo.Ext + "\""); 
      response.AddHeader("Content-Length", fileInfo.SizeInBytes.ToString()); 
      response.ContentType = "application/octet-stream"; 
      logic.GetDownloadStreamSync(id, response.OutputStream); 
      response.StatusCode = (int)HttpStatusCode.OK; 
      //HttpContext.Current.ApplicationInstance.CompleteRequest(); 
      response.End(); 
     } 
     catch(Exception ex) 
     { 
      Console.WriteLine(ex.ToString()); 
     } 
    } 

Ve şöyle GetDownloadStreamSync bağlıdır: (Fiddler bağlantısı iptal edildi bildiriyor) çalışma Senkron koddur -----

zaman uyumsuz sürümü hariç tamamen aynı olduğu: GetDo eşzamansız uygulamasıyla

public async Task Get(int id) 
{ 
    FileInfo fileInfo = await logic.GetFileInfoSync(id); // database opp 
      HttpResponse response = HttpContext.Current.Response; 
      response.Clear(); 
      response.ClearContent(); 
      response.Buffer = true; 
      response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.Node.Name + fileInfo.Ext + "\""); 
      response.AddHeader("Content-Length", fileInfo.SizeInBytes.ToString()); 
      response.ContentType = "application/octet-stream"; 
      await logic.GetDownloadStreamSync(id, response.OutputStream); 
          //database opp + file I/O 
      response.StatusCode = (int)HttpStatusCode.OK; 
      //HttpContext.Current.ApplicationInstance.CompleteRequest(); 
      response.End(); 
} 

wnloadStream şöyle: yani umarım birileri bu neden başarısız olacağını farkındadır Biz önden arkaya doğru zaman uyumsuz/bekliyoruz desen kucaklamaya çalışıyoruz

public async Task GetDownloadStream(string fileIdentifier, Stream streamToCopyTo) 
{ 
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, true)) 
    { 
     await fs.CopyToAsync(streamToCopyTo); 
    } 
} 

(streamToCopyTo response.OutputStream gelen OutputStream olan)? Ayrıca Response.End(), Response.Flush() ve HttpContext.Current.ApplicationInstance.CompleteRequest() çağırmayı denedim. Ayrıca, aşağıdaki sorulara/yorumlara yanıt olarak, GetDownloadStream yöntemine isabet edilmemesi sonucu response.End() üzerinde bir kesme noktası koymuştum. Belki de OutputStream asenkron değil mi? Herhangi bir fikir açığız! Teşekkürler

************************** Son Çözüm **************** ***********

Yorum yapan herkese ve özellikle de FileOptions.DeleteOnClose hakkındaki önerisi için @Noseratio'ya büyük teşekkürler. GetDownloadStreamAsync böyle bakarken

[HttpGet] 
public async Task<HttpResponseMessage> Get(long id) 
{ 
     HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); 
     Node node = await logic.GetFileInfoForNodeAsync(id); 

     result.Content = new StreamContent(await logic.GetDownloadStreamAsync(id)); 
     result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 
     result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") 
     { 
      FileName = node.Name + node.FileInfo.Extension 
     }; 
     result.Content.Headers.ContentLength = node.FileInfo.SizeInBytes; 
     return result 
} 

:

FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, FileOptions.DeleteOnClose | FileOptions.Asynchronous); 
ben de anında dosya akışı şifresini edildi ve bu, çalışır dışarı bıraktı

ilgilenenler ...

için çok
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, FileOptions.DeleteOnClose | FileOptions.Asynchronous); 
RijndaelManaged rm = new RijndaelManaged(); 
return new CryptoStream(fs, GetDecryptor(rm, password), CryptoStreamMode.Read); 
+0

MSDN'ın açıklama budur. "Bir o üzerinde çalıştığı konu engellemez ifadesini bekliyor Bunun yerine, neden derleyici async metodunun geri kalanını, beklenen bir görev olarak devam ettirmek için kaydolur.Daha sonra async metodunun arayıcısına geri döner.Göre tamamlandığında, devam eder ve async metodunun icrası kaldığı yerden devam eder. Bekleyen bir ifade, yalnızca bir derleyici, lambda ifadesi veya bir anync değiştirici tarafından işaretlenen anonim bir yöntemde ortaya çıkabilir. Başka bir yerde, bir tanımlayıcı olarak yorumlanır. " – user1789573

+0

Yöntem async imzası nedir dava? (Task Get ... 'i farz ediyorum, ama emin olun). Eğer söz konusu bir şey kaçırmak durumunda –

+0

, tam zaman uyumsuz sürümü gönderebilir miyim? – EZI

cevap

1

için bu makaleye bakın HttpContext.Current.ApplicationInstance.CompleteRequest()

kullanarak olmalıdır, ama ben zaten burada async/await gerek sanmıyorum . Ayrıca, özellikle de eşzamanlı olmayan WebAPI denetleyici yöntemlerinde HttpContext.Current.Response'u kullanmaktan kaçınmanız gerektiğini düşünüyorum.Bu özel durumda

, sen HttpResponseMessage kullanabilirsiniz:

[HttpGet] 
public HttpResponseMessage Get(int id) 
{ 
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); 
    FileInfo fileInfo = logic.GetFileInfoSync(id); 

    FileStream fs = new FileStream(
     filePath, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize, false); 

    result.Content = new StreamContent(fs); 
    result.Content.Headers.ContentType = 
     new MediaTypeHeaderValue("application/octet-stream"); 
    result.Content.Headers.ContentDisposition = 
     new ContentDispositionHeaderValue("attachment") 
     { 
      FileName = fileInfo.Node.Name + fileInfo.Ext 
     }; 
    result.Content.Headers.ContentLength = fileInfo.SizeInBytes; 

    return result; 
} 

burada hiçbir açık asenkroni yoktur, bu yüzden yöntem async değil. Eğer hala bazı await tanıtmak gerekir ancak, yöntem böyle isterseniz:

İşte
[HttpGet] 
public async Task<HttpResponseMessage> Get(int id) 
{ 
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); 
    // ... 
    await fs.CopyToAsync(streamToCopyTo) 
    // ... 
    return result; 
} 
+0

İlk çözümle ilgili soru, FileStream'i açık bırakıyor mu? Yukarıdaki çözümüm, kapandığını ve düzgün bir şekilde bertaraf edilmesini sağlamak için etrafında güzel bir kullanım ifadesine sahipken, ilk çözümde FileStream'in açık bırakılmasından biraz endişeliyim, bu doğru mu? FileStream'i bir dosyaya sarmak ve dosya indirildikten sonra yerel sürücüden dosyayı silmeye çalışmak daha mantıklı geliyor mu? – TChadwick

+1

@TChadwick, hayır açık kalmayacak, WebAPI çalışma zamanı, bittiğinde akışta "Kapat" diyecektir. Bu noktada silmeniz gerekirse, sadece 'FileOptions.DeleteOnClose | FileOptions.Asynchronous ', yeni FileStream' için son parametre olarak. – Noseratio

+0

@TChadwick, bu yaklaşımı işe aldın mı? – Noseratio

1

Sorununuzun kökü aslında Response.End() kullanımında yatar. Async'i çalıştırdığınızda, dosya içeriğini yürütmeden önce Response.End()'u çalıştıracaktır. Bu, Sync sürümünü kullanırken görülemez, çünkü dosya içeriğinin akışı tamamlanana kadar Response.End() çağrılmaz.

Response.End(), bir TreadAbortException atarken işleminizi tamamladığınızı söylemenin AŞAklık açısından kötü bir yoludur. Bunun yerine, daha fazla bilgi Bu sizin tam soruyu cevaplamak için tam bir üreme durum var etmek alırdım Response.End, Response.Close, and How Customer Feedback Helps Us Improve MSDN Documentation

+0

Cevabınız için teşekkür ederiz Matthew. Response.End() ile veya olmadan, sorun aynı, ben de CompleteRequest() de denedim. – TChadwick

+0

@TChadwick, bu şimdiye HTTP Response yoluyla müşteriye akışı dosyalarını yardımcı olmak için geçenlerde yazdığı bir içerik yardımcıdır. http://pastebin.com/5B29Pgdp Bu, uyumsuz değil, ancak bir uyumsuz bağlamında çalışmalıdır. Deneyin ve işe yarayıp yaramadığını görün. –

+0

Ben bu işe daha da kazdık, Yanıtlarınızla için tekrar teşekkür ederim, ben Sync bu aynı çağrıları çalıştırdığınızda, sorunsuz çalışırlar. Calling çağrısının aslında 200 yerine 204 (No Content) olarak değiştirildiğini ve tarayıcının dosyanın indirilmemesine neden olduğunu ve Response.End() işlevini 200 kodu döndürmek için çağırmam gerektiğini buldum. Çalıştır. Dosyayı async olarak okumaya ve OutputStream'e async olarak yazmayı isterim, fakat bu noktada gerçekçi görünmüyor ... – TChadwick