web-dev-qa-db-fra.com

Existe-t-il un moyen de convertir un System.IO.Stream en un Windows.Storage.Streams.IRandomAccessStream?

Sous Windows 8; Je voudrais transmettre le contenu d'un MemoryStream à une classe qui accepte un paramètre de type Windows.Storage.Streams.IRandomAccessStream. Existe-t-il un moyen de convertir ce MemoryStream en IRandomAccessStream?

52
bbosak

Pour utiliser les extensions: vous devez ajouter "using System.IO"

Dans Windows8, les types .NET et WinRT sont généralement convertis vers/à partir de types compatibles sous le capot, vous n'avez donc pas à vous en soucier.

Pour les flux, cependant, il existe des méthodes d'assistance pour convertir entre les flux WinRT et .NET: Pour la conversion à partir de flux WinRT -> flux .NET:

InMemoryRandomAccessStream win8Stream = GetData(); // Get a data stream from somewhere.
System.IO.Stream inputStream = win8Stream.AsStream()

Pour convertir à partir de flux .NET -> flux WinRT:

Windows.Storage.Streams.IInputStream inStream = stream.AsInputStream();
Windows.Storage.Streams.IOutputStream outStream = stream.AsOutputStream();

MISE À JOUR: 01/09/2013

Qu'on ne dise pas que Microsoft n'écoute pas sa communauté de développeurs;)

Dans le annonce pour .NET FX 4.5.1 , Microsoft déclare que:

Beaucoup d'entre vous voulaient un moyen de convertir un flux .NET en IRandomAccessStream Windows Runtime. Appelons-le simplement une méthode d'extension AsRandomAccessStream. Nous n'avons pas pu intégrer cette fonctionnalité dans Windows 8, mais c'était l'un de nos premiers ajouts à Windows 8.1 Preview.

Vous pouvez maintenant écrire le code suivant, pour télécharger une image avec HttpClient, la charger dans un BitmapImage puis définir comme source pour un contrôle Xaml Image.

    //access image via networking i/o
    var imageUrl = "http://www.Microsoft.com/global/en-us/news/publishingimages/logos/MSFT_logo_Web.jpg";
    var client = new HttpClient();
    Stream stream = await client.GetStreamAsync(imageUrl);
    var memStream = new MemoryStream();
    await stream.CopyToAsync(memStream);
    memStream.Position = 0;
    var bitmap = new BitmapImage();
    bitmap.SetSource(memStream.AsRandomAccessStream());
    image.Source = bitmap;

HTH.

93
Rich Turner

Trouvé une solution plus élégante:

public static class MicrosoftStreamExtensions
{
    public static IRandomAccessStream AsRandomAccessStream(this Stream stream)
    {
        return new RandomStream(stream);
    }

}

class RandomStream : IRandomAccessStream
{
    Stream internstream;

    public RandomStream(Stream underlyingstream)
    {
        internstream = underlyingstream;
    }

    public IInputStream GetInputStreamAt(ulong position)
    {
        //THANKS Microsoft! This is GREATLY appreciated!
        internstream.Position = (long)position;
        return internstream.AsInputStream();
    }

    public IOutputStream GetOutputStreamAt(ulong position)
    {
        internstream.Position = (long)position;
        return internstream.AsOutputStream();
    }

    public ulong Size
    {
        get
        {
            return (ulong)internstream.Length;
        }
        set
        {
            internstream.SetLength((long)value);
        }
    }

    public bool CanRead
    {
        get { return this.internstream.CanRead; }
    }

    public bool CanWrite
    {
        get { return this.internstream.CanWrite; }
    }

    public IRandomAccessStream CloneStream()
    {
        throw new NotSupportedException();
    }

    public ulong Position
    {
        get { return (ulong)this.internstream.Position; }
    }

    public void Seek(ulong position)
    {
        this.internstream.Seek((long)position, SeekOrigin.Begin);
    }

    public void Dispose()
    {
        this.internstream.Dispose();
    }

    public Windows.Foundation.IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
    {
        return this.GetInputStreamAt(this.Position).ReadAsync(buffer, count, options);
    }

    public Windows.Foundation.IAsyncOperation FlushAsync()
    {
        return this.GetOutputStreamAt(this.Position).FlushAsync();
    }

    public Windows.Foundation.IAsyncOperationWithProgress WriteAsync(IBuffer buffer)
    {
        return this.GetOutputStreamAt(this.Position).WriteAsync(buffer);
    }
}
7
bbosak

Après quelques essais, j'ai trouvé que le code suivant fonctionnait.

using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage.Streams;

partial class MainPage
{
    public MainPage()
    {
        var memoryStream = new MemoryStream(new byte[] { 65, 66, 67 });
        ConvertToRandomAccessStream(memoryStream, UseRandomAccessStream);
        InitializeComponent();
    }

    void UseRandomAccessStream(IRandomAccessStream stream)
    {
        var size = stream.Size;
    } // put breakpoint here to check size

    private static async void ConvertToRandomAccessStream(MemoryStream memoryStream,
         Action<IRandomAccessStream> callback)
    {
        var randomAccessStream = new InMemoryRandomAccessStream();
        var outputStream = randomAccessStream.GetOutputStreamAt(0);
        var dw = new DataWriter(outputStream);
        var task = new Task(() => dw.WriteBytes(memoryStream.ToArray()));
        task.Start();
        await task;
        await dw.StoreAsync();
        var success = await outputStream.FlushAsync();
        callback(randomAccessStream);
    }
}

PDATE: J'ai également essayé une implémentation de méthode plus élégante:

    private static void ConvertToRandomAccessStream(MemoryStream memoryStream,
        Action<IRandomAccessStream> callback)
    {
        var randomAccessStream = new InMemoryRandomAccessStream();
        var outputStream = randomAccessStream.GetOutputStreamAt(0);
        RandomAccessStream.Copy(memoryStream.AsInputStream(), outputStream);
        callback(randomAccessStream);
    }

Étrangement, cela ne fonctionne pas. Quand j'appelle stream.Size plus tard, je reçois zéro.

MISE À JOUR J'ai changé la fonction pour retourner l'IRandomAccessStream plutôt que d'utiliser la fonction de rappel

public static async Task<IRandomAccessStream> ConvertToRandomAccessStream(MemoryStream memoryStream)
{
    var randomAccessStream = new InMemoryRandomAccessStream();

    var outputStream = randomAccessStream.GetOutputStreamAt(0);
    var dw = new DataWriter(outputStream);
    var task = new Task(() => dw.WriteBytes(memoryStream.ToArray()));
    task.Start();

    await task;
    await dw.StoreAsync();

    await outputStream.FlushAsync();

    return randomAccessStream;
}
5
Roman Boiko

Il n'y a pas de méthode intégrée sur Windows 8. Pour Windows 8.1, nous avons ajouté une méthode d'extension Stream.AsRandomAccessStream ():

internal static IRandomAccessStream ToRandomAccessStream(byte[] array)
{
    MemoryStream stream = new MemoryStream(array);
    return stream.AsRandomAccessStream();
}
4
Immo Landwerth

Rien de ce qui précède ne fonctionne pour moi aujourd'hui (peut-être quelques changements d'API depuis que les réponses ont été publiées). La seule façon qui fonctionne est

IRandomAccessStream inMemoryStream = new InMemoryRandomAccessStream();
using (var inputStream = stream.AsInputStream())
{
    await RandomAccessStream.CopyAsync(inputStream, inMemoryStream);
}
inMemoryStream.Seek(0);
3
Igor Kulman

Cet extrait de code transforme un flux (stream) en un InMemoryRandomAccessStream (ims) qui implémente IRandomAccessStream. L'astuce est que CopyTo doit être appelé sur un thread d'arrière-plan.

        InMemoryRandomAccessStream ims = new InMemoryRandomAccessStream();
        var imsWriter = ims.OpenWrite();
        await Task.Factory.StartNew(() => stream.CopyTo(imsWriter));
0
Robert Levy

Jetez un oeil à ce lien:

Comment convertir un tableau d'octets en IRandomAccessStream

Il donne également des exemples et une implémentation d'un constructeur de tableau d'octets (et un pour les flux .NET), utile si vous souhaitez utiliser les méthodes SetSource ou SetSourceAsync de la classe BitmapImage (comme dans mon cas).

J'espère que cela aide quelqu'un ...

0
MAXE