Forum Cross Platform with Xamarin

Best strategy to transfer photos to server

Hi, I am trying to solve an issue that impacts my app performance. I have a gallery app that user can select photos to build an album. Once the album is build the app transfers selected photos to the server. On the server side we expect 1200 x 400 /300DPI images. The server has functionality to resize and change the resolution of the photos before printing. My problem is with transferring photo files. For example IOS7 photos are much bigger, so before transfer, I resize them to the expected size on the server and then gzip compress and send to server. Still this corresponds to 3-8 MB size transfers and it is too slow. What could be my options to make super fast transfers without loosing on photo quality that much.

Answers

  • ⦁   Instalar el Nuget Xam.Plugin.Media:
                        NewtonSoft este es para serializar  objetos 
    
    ![](https://us.v-cdn.net/5019960/uploads/editor/yd/1yb41qqc11un.jpg "")
    
    
    ⦁   En el proyecto Droid en el archivo AndroidManifest.xml se agregar estas líneas, debe quedar:
        <?xml version="1.0" encoding="utf-8"?>
        <manifest xmlns:android="http://schemas.android.com/apk/res/android">
          <uses-sdk android:minSdkVersion="15" />
          <uses-permission android:name="android.permission.INTERNET" />
          <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
          <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
          <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
          <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
          <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
          <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
          <uses-permission android:name="android.permission.CAMERA" />
          <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
          <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
          <application android:label="MapsConcept.Droid">
              </application>
        </manifest>
    
    
    para darle permiso a la cámara....
    
    
    ⦁   En el proyecto iOS en el archivo Info.plist, se agregar estas líneas:
            </array>
            <key>UILaunchStoryboardName</key>
            <string>LaunchScreen</string>
            <key>NSCameraUsageDescription</key>
            <key>NSPhotoLibraryUsageDescription</key>
          </dict>
        </plist>
    
    
    
    ⦁   En los proyectos  Windows en el archivo App.xaml.cs en el evento OnLaunched agregamos estas lineas:
        }
    
        Plugin.Media.MediaImplementation.OnFilesPicked(e);
        base.OnActivated(e);
    
        // Ensure the current window is active
        Window.Current.Activate();
    }
    
    
    
    ⦁   En la página donde vamos a tomar y mostar la foto:
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
                 x:Class="MapsConcept.MapPage"
                 BindingContext="{Binding Main, Source={StaticResource Locator}}">
      <StackLayout>
        <Button 
          Command="{Binding TakePictureCommand}" 
          Text="Tomar Foto" />
        <Image
          WidthRequest="300"
          HeightRequest="300"
          Source="{Binding ImageSource}" 
          VerticalOptions="CenterAndExpand" />
      </StackLayout>
    </ContentPage>
    
    
    ⦁   En el ViewModel, implementamos la interfaz:
    public class MainViewModel : INotifyPropertyChanged
    Creamos el evento:
    public event PropertyChangedEventHandler PropertyChanged;
    El atributo:
    private ImageSource imageSource;
    La propiedad:
    public ImageSource ImageSource
    {
        set
        {
            if (imageSource != value)
            {
                imageSource = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ImageSource"));
            }
        }
        get
        {
            return imageSource;
        }
    }
    El comando:
    public ICommand TakePictureCommand { get { return new RelayCommand(TakePicture); } }
    El método:
    private async void TakePicture()
    {
        await CrossMedia.Current.Initialize();
    
        if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
        {
            await App.Current.MainPage.DisplayAlert("No Camera", ":( No camera available.", "Aceptar");
        }
    
        var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
        {
            Directory = "Sample",
            Name = "test.jpg"
        });
    
        if (file != null)
        {
            ImageSource = ImageSource.FromStream(() =>
            {
                var stream = file.GetStream();
                file.Dispose();
                return stream;
            });
        }
    }
    
    
    hasta aquí solo hemos tomado la foto y mostrarla ahora tenemos que subirla al server...
    
    
    
            public async Task<Response> SetPhotoAsync(int valor, Stream stream)
            {
                try
                {
    
                    var array = ReadFully(stream);
    
                    var photoRequest = new PhotoRequest
                    {
                        Id = multaId,
                        Array = array,
                    };
    
                    var request = JsonConvert.SerializeObject(photoRequest);
                    var body = new StringContent(request, Encoding.UTF8, "application/json");
                    var client = new HttpClient();
                    client.BaseAddress = new Uri("Uri del Server");
                    var url = "/api/Metodoparasubirimagen";
                    var response = await client.PostAsync(url, body);
    
                    if (!response.IsSuccessStatusCode)
                    {
                        return new Response
                        {
                            IsSuccess = false,
                            Message = response.StatusCode.ToString(),
                        };
                    }
    
                    return new Response
                    {
                        IsSuccess = true,
                        Message = "Foto asignada Ok",
                    };
                }
                catch (Exception ex)
                {
                    return new Response
                    {
                        IsSuccess = false,
                        Message = ex.Message,
                    };
                }
    
            }
    
    //Convierte el stream de la foto a un arreglo de byte para poder serializarlo con Json
            public static byte[] ReadFully(Stream input)
            {
                byte[] buffer = new byte[16 * 1024];
                using (MemoryStream ms = new MemoryStream())
                {
                    int read;
                    while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        ms.Write(buffer, 0, read);
                    }
                    return ms.ToArray();
                }
            }
    
    
    
    
    Y ahora el método en el Api
    
    
     [HttpPost]
            [Route("SetFoto")]
            public IHttpActionResult SetFoto(FotoRequest fotoRequest)
            {
                db.Configuration.ProxyCreationEnabled = false;
    
    ///Se  convierte de un arreglo de byte a un stream es el proceso contrario a lo que se hace en el dispositivo ...
                var stream = new MemoryStream(fotoRequest.Array);
    
                var file = string.Format("{0}.jpg", fotoRequest.Id);
                var folder = "~/Content/CarpetaDondeSeVaAGuardar";
                var fullPath = string.Format("{0}/{1}",folder,file);
    
                var response = FileHelper.UploadFoto(stream,folder,file);
    
                if (!response)
                {
                    BadRequest("Imagen de la multa no se pudo subir al servidor...");
                }
    
            }
    
    
    
    El método que copia la imagen en el server.....
    
    
    public class FileHelper
        {
    
            public static bool UploadFoto(MemoryStream stream, string folder, string name)
            {
                try
                {
                    stream.Position = 0;
                    var path = Path.Combine(HttpContext.Current.Server.MapPath(folder),name);
                    File.WriteAllBytes(path, stream.ToArray());
                }
                catch
                {
    
                    return false;
                }
                return true;
    
            }
        }
    
  • AndColomboAndColombo ITMember ✭✭

    to send an image you can convert it to a byte array and then convert it to json.
    To reduce image size you can try something like this:

    create a custom renderer for each platform

    For iOS:
    ` public void ResizeImage()
    {

    App.CroppedImage = image.AsJPEG().ToArray(); //convert a UIImage into a byte array
    
    if (App.CroppedImage.Length > 490000)
    {
        while (App.CroppedImage.Length > 490000)
        { 
            App.CroppedImage = image.AsJPEG((nfloat)0.7).ToArray();//save the compressed image as a byte array into a field in App.cs
            using (var data = NSData.FromArray(App.CroppedImage))
            {
                image = UIImage.LoadFromData(data);
            }
        }
    }
    

    } `

    For android:
    ` public void ResizeImage()
    {

    Bitmap cropped = YourBitmapImage;
    
    if (App.CroppedImage.Length > 490000) //max image size in byte (490KB)
    {
        while (App.CroppedImage.Length > 490000)
        {
            using (MemoryStream memory = new MemoryStream())
            {
                cropped.Compress(Bitmap.CompressFormat.Jpeg, 70, memory);
                App.CroppedImage = memory.ToArray(); //save the compressed image as a byte array into a field in App.cs
            }
            cropped = BitmapFactory.DecodeByteArray(App.CroppedImage, 0, App.CroppedImage.Length);
        }
    }
    

    } `

Sign In or Register to comment.