Question Retour du fichier binaire du contrôleur dans l'API Web ASP.NET


Je travaille sur un service Web utilisant le nouveau WebAPI d'ASP.NET MVC qui servira des fichiers binaires, principalement .cab et .exe des dossiers.

La méthode de contrôleur suivante semble fonctionner, ce qui signifie qu’elle renvoie un fichier, mais que le type de contenu est défini sur application/json:

public HttpResponseMessage<Stream> Post(string version, string environment, string filetype)
{
    var path = @"C:\Temp\test.exe";
    var stream = new FileStream(path, FileMode.Open);
    return new HttpResponseMessage<Stream>(stream, new MediaTypeHeaderValue("application/octet-stream"));
}

Y a-t-il une meilleure manière de faire cela?


275
2018-03-02 22:37


origine


Réponses:


Essayez d'utiliser un simple HttpResponseMessage avec son Content propriété définie sur StreamContent:

// using System.IO;
// using System.Net.Http;
// using System.Net.Http.Headers;

public HttpResponseMessage Post(string version, string environment,
    string filetype)
{
    var path = @"C:\Temp\test.exe";
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
    result.Content = new StreamContent(stream);
    result.Content.Headers.ContentType = 
        new MediaTypeHeaderValue("application/octet-stream");
    return result;
}

Quelques remarques à propos de la stream utilisé:

  • Vous ne devez pas appeler stream.Dispose(), car l'API Web doit toujours pouvoir y accéder lorsqu'elle traite la méthode du contrôleur result pour renvoyer des données au client. Par conséquent, n'utilisez pas de using (var stream = …) bloc. L'API Web mettra le flux à votre disposition.

  • Assurez-vous que le flux a sa position actuelle définie sur 0 (c'est-à-dire le début des données du flux). Dans l'exemple ci-dessus, c'est un fait puisque vous venez d'ouvrir le fichier. Cependant, dans d'autres scénarios (par exemple, lorsque vous écrivez d'abord des données binaires dans un MemoryStream), assurez-vous de stream.Seek(0, SeekOrigin.Begin); ou définir stream.Position = 0;

  • Avec les flux de fichiers, spécifiant explicitement FileAccess.Read l'autorisation peut aider à prévenir les problèmes de droits d'accès sur les serveurs Web; Les comptes de pool d'applications IIS reçoivent souvent uniquement des droits d'accès en lecture / liste / exécution sur wwwroot.


448
2018-03-03 21:09



Pour API Web 2, vous pouvez mettre en œuvre IHttpActionResult. Voici la mienne:

class FileResult : IHttpActionResult
{
    private readonly string _filePath;
    private readonly string _contentType;

    public FileResult(string filePath, string contentType = null)
    {
        if (filePath == null) throw new ArgumentNullException("filePath");

        _filePath = filePath;
        _contentType = contentType;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StreamContent(File.OpenRead(_filePath))
        };

        var contentType = _contentType ?? MimeMapping.GetMimeMapping(Path.GetExtension(_filePath));
        response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);

        return Task.FromResult(response);
    }
}

Alors quelque chose comme ça dans votre contrôleur:

[Route("Images/{*imagePath}")]
public IHttpActionResult GetImage(string imagePath)
{
    var serverPath = Path.Combine(_rootPath, imagePath);
    var fileInfo = new FileInfo(serverPath);

    return !fileInfo.Exists
        ? (IHttpActionResult) NotFound()
        : new FileResult(fileInfo.FullName);
}

Et ici, vous pouvez dire à IIS d’ignorer les requêtes avec une extension pour que la requête parvienne au contrôleur:

<!-- web.config -->
<system.webServer>
  <modules runAllManagedModulesForAllRequests="true"/>

109
2018-01-02 17:52



Bien que la solution proposée fonctionne correctement, il existe un autre moyen de renvoyer un tableau d'octets à partir du contrôleur, avec un flux de réponses correctement formaté:

  • Dans la requête, définissez l'en-tête "Accept: application / octet-stream".
  • Côté serveur, ajoutez un formateur de type de support pour prendre en charge ce type mime.

Malheureusement, WebApi n'inclut aucun formateur pour "application / octet-stream". Il y a une implémentation ici sur GitHub: BinaryMediaTypeFormatter (Il existe des adaptations mineures pour que cela fonctionne pour webapi 2, les signatures de méthode ont été modifiées).

Vous pouvez ajouter ce formateur dans votre configuration globale:

HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));

WebApi devrait maintenant utiliser BinaryMediaTypeFormatter si la requête spécifie l'en-tête Accept correct.

Je préfère cette solution car un contrôleur d'action retournant byte [] est plus confortable à tester. Cependant, l'autre solution vous permet plus de contrôle si vous voulez retourner un autre type de contenu que "application / octet-stream" (par exemple "image / gif").


8
2017-07-29 16:03



La surcharge que vous utilisez définit l'énumération des formateurs de sérialisation. Vous devez spécifier le type de contenu explicitement comme:

httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

6
2018-03-02 22:58



Pour toute personne ayant le problème de l'appel de l'API plusieurs fois lors du téléchargement d'un fichier assez volumineux en utilisant la méthode de la réponse acceptée, définissez la mise en mémoire tampon de la réponse sur true     System.Web.HttpContext.Current.Response.Buffer = true;

Cela garantit que tout le contenu binaire est mis en mémoire tampon côté serveur avant d'être envoyé au client. Sinon, plusieurs requêtes seront envoyées au contrôleur et si vous ne le gérez pas correctement, le fichier sera corrompu.


6
2017-11-28 04:41



Tu pourrais essayer

httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");

3
2017-08-01 15:23