WCF Service - Bilder übertragen



  • Hallo,

    ich erkläre am besten mal meine Anforderung:

    Ich habe einen WCF-Service auf den ich von einer Silverlight Application aus zugreife. Innerhalb der Domäne des Services existiert ein Image-Folder. Dies ist alles was statisch existiert.
    In diesem Verzeichnis können weitere Folder abgelegt werden. Diese repräsentieren einzelne Alben. Der Name des Folder entspricht dann auch dem Titel des Albums.

    Die eigentliche Übertragung der Bilder stellt die größte Frage dar. Aus meiner Sicht würde ich einfach die Raw-Daten der Dateien übers Netzwerk übertragen. Da jedoch in WCF der ganze Netzwerkverkehr stark abstrahiert wurde, gibt es sicherlich eine viel elegantere Möglichkeit. Am einfachsten wäre wohl die Übertragung eines BitmapImage[]. Dazu würde sich mir aber auch die Frage stellen, wie ich das mache, da meines Wissens nach nur Klassen übertragen werden können, die das DataContractAttribute-Attribut haben.
    Am liebsten wäre mir eine Art Random-Access-Iterator auf meine Images zurückgeliefert zu bekommen. Das sollte so funktionieren, dass man dem Konstruktor einen PrefetchLevel mitgeben kann.
    Angenommen ich möchte 16 Bilder prefetchen, dann sollte der erste Zugriff auf Image[0] mir die Bilder 0-15 übertragen, sodass erst bei Zugriff auf Image[16] erneut die Netzwerkleitung in Anspruch genommen werden müsste.

    Die Umsetzung stelle ich mir ungefähr so vor (PseudoCode)

    WcfServiceClient client = new WcfServiceClient();
    
    MyAlbumEnum album    = client.EnumerateAlbums();			// alle Alben im Ordner Image
    MyImageIterator iter = client.EnumerateImages(album.AlbumA, 16);
    
    [...]
    

    Hat da jemand eine Lösung parat, die in diese Richtung geht?
    Vielen Dank im Voraus.



  • Bei einer ähnlichen Aufgabe hat mir seinerzeit dieser Artikel weitergeholfen. Vielleicht hilft er dir ja auch ...?

    Wichtiger Nachtrag: Vom Datenvertragsserialisierer unterstützte Typen



  • Vom Datenvertragsserialisierer unterstützte Typen schrieb:

    Alle öffentlich sichtbaren Typen, die über einen Konstruktor ohne Parameter verfügen.

    Damit wäre ja auch (z.B.) BitmapImage abgedeckt, wenn ich das richtig deute.

    Erstellen eines Diensts, der beliebige Daten mithilfe des WCF-Webprogrammiermodells zurückgibt schrieb:

    public class Service : IImageServer
        {
            public Stream GetImage(int width, int height)
            {
                Bitmap bitmap = new Bitmap(width, height);
                for (int i = 0; i < bitmap.Width; i++)
                {
                    for (int j = 0; j < bitmap.Height; j++)
                    {
                        bitmap.SetPixel(i, j, (Math.Abs(i - j) < 2) ? Color.Blue : Color.Yellow);
                    }
                }
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                ms.Position = 0;
                WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
                return ms;
            }
        }
    

    Also wäre das ganze Verfahren dann doch in ungefähr so, wie ich es mir aus unteren Ebenen heraus vorgestellt habe. Man übertragt die Bilder als ByteStream.
    Ist das wirklich die eleganteste Art? Ich kann also nicht direkt als BitmapImage übertragen? Gut, soll egal sein, die Umwandlung ist immer nur eine Zeile Code.

    Viel interessanter: Prefetching-Mechanismen, wie ich sie beschrieben habe, müsste ich mir also selber basteln?!

    Ich werde mal etwas basteln und den Code dann hier posten!



  • FrEEzE2046 schrieb:

    Viel interessanter: Prefetching-Mechanismen, wie ich sie beschrieben habe, müsste ich mir also selber basteln?!

    Es gibt da einige Patterns die mit Fowler in Zusammenhang gebracht werden. Du kannst mal nach Data Transfer Object und Remote Facade googlen und schauen ob du Anregungen findest.
    Ich würde den Service "dumm" gestalten, der den Startindex und die Anzahl bekommt und eine Image-Serie liefert. Im Client würde ich ein Proxy bauen der den Cache-Mechanismus implementiert: Du holst aus dem Proxy die Bilder einzeln raus und der Proxy fordert aber immer gleich 16 beim Service an falls du ein Bild brauchst das er nicht vorrätig hat.



  • Ich bin einem Beispiel auf MSDN gefolgt und wollte erst mal die grundlegende Übertragung implementierten. Der Service sieht folgendermaßen aus:

    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class ImageService
    {
        private IEnumerable<FileInfo> _Files;
    
        public ImageService()
        {
            this._Files = new DirectoryInfo(@"F:\Images").EnumerateFiles();
        }
    
        [OperationContract, WebGet, FaultContract(typeof(ArgumentOutOfRangeException))]
        public Stream GetImage(uint num)
        {
            if (this._Files.Count() > num)
            {
                MemoryStream ms = new MemoryStream();
                Bitmap bmp = new Bitmap(this._Files.ElementAt((int)num).OpenRead());
                bmp.Save(ms, ImageFormat.Jpeg);
                ms.Position = 0;
                WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
                return ms;
            }
            else
                throw new FaultException<ArgumentOutOfRangeException>(new ArgumentOutOfRangeException());
        }
    }
    

    Der Aufruf so:

    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {         
        this.stateText.Text = "begin loading image";
        var client = new ImageServiceClient();
    
        var obsGetImage = Observable.FromEvent<GetImageCompletedEventArgs>
        (
            h => client.GetImageCompleted += h,
            h => client.GetImageCompleted -= h
        )
        .Take(1);
    
        obsGetImage.Subscribe
        (
            args =>
            {
                BitmapImage bmp = new BitmapImage();
    
                try
                {
                    bmp.SetSource(new MemoryStream(args.EventArgs.Result));
                    this.img.Source = bmp;
                }
                catch (Exception ex)
                {
                    do
                    {
                        MessageBox.Show(ex.Message);
                    }
                    while( (ex = ex.InnerException) != null );
                }
    
                this.stateText.Text = "finished loading image";
            }
        );
    
        client.GetImageAsync(0);
    }
    

    Ich bekomme immer folgende Exceptions:

    ex = Während des Vorgangs ist eine Ausnahme aufgetreten, sodass das Ergebnis ungültig ist. Weitere Ausnahmedetails finden Sie in InnerException.
    ex.InnerException = Der Inhaltstyp "image/jpeg" der Antwortnachricht stimmt nicht mit dem Inhaltstyp der Bindung (application/soap+msbin1) überein. Wenn sie einen benutzerdefinierten Encoder verwenden, sollten Sie sicherstellen, dass die IsContentTypeSupported-Methode korrekt implementiert ist. Dier ersten 1024 Bytes der Antwort waren ...
    

    Warum der Fehler auftritt ist mir klar, aber wie kann ich den "Inhaltstyp der Bindung" definieren?


Log in to reply