I having an issue uploading / sending images from a stream to a generic handler in ASP.NET. I’ve build a windows phone app that can take an image and send it to the generic handler on my website.
Unfortunately in some occasions the image turns out to be (partially) gray. I think it might have something to to with a lost/bad internet connection of the mobile device. This problem happens every 50 images or so.
When the faulty image is send I do not get an error of any kind. I’m looking for two possible solutions.
How do i prevent the windows phone uploading a partially gray image to the generic handler.
How do i check if an image is partially gray on the server so can send a error message back to the phone.
to make this question more compleet I included the code of the generic handler and an example image. Second I’m very curious why this occus. TCPIP has an handshake so above isseu should not be possible ?
public class UploadImages : IHttpHandler
{
private IWorkOrderRepository _workOrderRepository;
private IDigitalFileRepository _digitalFileRepository;
private IUserRepository _userRepository;
public UploadImages()
{
_workOrderRepository = ((Global)HttpContext.Current.ApplicationInstance).Kernel.Get<IWorkOrderRepository>();
_digitalFileRepository = ((Global)HttpContext.Current.ApplicationInstance).Kernel.Get<IDigitalFileRepository>();
_userRepository = ((Global)HttpContext.Current.ApplicationInstance).Kernel.Get<IUserRepository>();
}
public void ProcessRequest(HttpContext context)
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var user = (Domain.Users.User)HttpContext.Current.User;
string WorkOrderId = context.Request.QueryString["workOrderId"];
string latitude = context.Request.QueryString["LATITUDE"];
string Longitude = context.Request.QueryString["LONGITUDE"];
if (latitude != "0" && Longitude != "0")
{
string file = "Filename.jpg";
string uploadPath = context.Server.MapPath("~/Temp/");
using (var stream = new MemoryStream())
{
var image = ImageResizer.Resize(Image.FromStream(context.Request.InputStream), 700);
image.Save(stream, ImageFormat.Jpeg);
stream.Position = 0;
var workOrder = _workOrderRepository.GetAll(x => x.Id == Convert.ToInt32(WorkOrderId)).FirstOrDefault();
workOrder.AddPhoto(_workOrderRepository, _digitalFileRepository, new AuditInfo((Domain.Users.User)user), stream, file, "image/jpeg", Convert.ToDouble(latitude), Convert.ToDouble(Longitude));
}
}
else
{
string file = "Filename.jpg";
string uploadPath = context.Server.MapPath("~/Temp/");
using (var stream = new MemoryStream())
{
var image = ImageResizer.Resize(Image.FromStream(context.Request.InputStream), 700);
image.Save(stream, ImageFormat.Jpeg);
stream.Position = 0;
var workOrder = _workOrderRepository.GetAll(x => x.Id == Convert.ToInt32(WorkOrderId)).FirstOrDefault();
workOrder.AddPhoto(_workOrderRepository, _digitalFileRepository, new AuditInfo((Domain.Users.User)user), stream, file, "image/jpeg");
}
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
Related
In the viewmodel of my page, I try to get an image to show. This image I get from one of our webservice. The image source is stored in ProductImage
string path = $#"http://www.MyCompany.be/cdn-cgi/image/width=150,quality=75/images/products/{CurrentProduct.Image}".Replace($"\\", $"/");
ProductImage = ImageSource.FromUri(new Uri(path));
I of course make sure when I update the Image source for the view when I change it
public ImageSource ProductImage
{
get
{
return _productImage;
}
set
{
if (_productImage != value)
{
_productImage = value;
OnPropertyChanged();
}
}
}
Sadly, for some reason it doesn't work. I checked the URL called and it does lead to an image.
It used to work when I was using streams instead of calling an URL. I know I haven't put the safety checks around yet to make sure the image exist, but other than that nothing has changed. I save the image the exact same way, i just use fromUri rather than fromStream.
//Get target's SmbFile.
var file = new SmbFile(path, auth);
try
{
if (file.Exists())
{
// Get readable stream.
var readStream = file.GetInputStream();
//Create reading buffer.
MemoryStream memStream = new MemoryStream();
//Get bytes.
((Stream)readStream).CopyTo(memStream);
var stream1 = new MemoryStream(memStream.ToArray());
if (stream1.Length < 30000000)
{
//Save image
//ProductImage = ImageSource.FromStream(() => stream1);
//Dispose readable stream.
readStream.Dispose();
InfoColSpan = 1;
}
else
{
Common.AlertError("Image trop lourde pour l'affichage");
}
}
}
catch (Exception ex)
{
Common.AlertError(ex, "Impossible de charger l'image");
}
Since I changed my method, I no longer load anything.
After upgrading CefSharp to Version 105.3.390 the browser doesn't display local pdf files anymore (displaying local html file works).
The browser window is black and Windows OS opens a window: "You'll need a new app to open this chrome-extension link".
As I am using a custom resource handler all urls look like "local://C:/path/to/file.pdf".
Settings:
var cefSettings = new CefSettings()
{
LogSeverity = LogSeverity.Verbose,
CachePath = SystemService.CefCacheDir,
LogFile = SystemService.CefSharpLogFile
};
CefCustomScheme localScheme = new CefCustomScheme
{
SchemeName = LocalSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new LocalSchemeHandlerFactory(),
IsCSPBypassing = true
};
cefSettings.RegisterScheme(localScheme);
CefSharp.Cef.Initialize(cefSettings);
LocalSchemeHandlerFactory:
public class LocalSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "local";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomSchemeHandler();
}
}
CustomSchemeHandler:
public class CustomSchemeHandler : ResourceHandler
{
public override CefReturnValue ProcessRequestAsync(IRequest request, ICallback callback)
{
string file = null;
var uri = new Uri(request.Url);
if (uri.Scheme == LocalSchemeHandlerFactory.SchemeName || uri.Scheme == SmbSchemeHandlerFactory.SchemeName)
{
if (uri.Scheme == LocalSchemeHandlerFactory.SchemeName)
{
var driveInfo = new DriveInfo(uri.Authority);
var path = uri.LocalPath.Substring(1);
file = Path.Combine(driveInfo.Name, path);
}
}
else
{
// handle invalid scheme
}
if (file == null)
return CefReturnValue.Cancel;
Task.Run(() =>
{
using (callback)
{
if (!File.Exists(file))
{
callback.Cancel();
return;
}
byte[] bytes = File.ReadAllBytes(file);
var stream = bytes != null ? new MemoryStream(bytes) : null;
if (stream == null)
{
callback.Cancel();
}
else
{
stream.Position = 0;
ResponseLength = stream.Length;
var fileExtension = Path.GetExtension(file);
MimeType = GetMimeType(fileExtension); // application/pdf
StatusCode = (int)HttpStatusCode.OK;
Stream = stream;
callback.Continue();
}
}
});
return CefReturnValue.ContinueAsync;
}
}
You find the cef log here.
What am I missing?
UPDATE: Similar to this, when trying to open dev tools in CefSharp, a window opens: "You'll need a new app to open this dev-tools link".
I found it myself. The reason was an implementation of RequestHandler:
internal class BrowserRequestHandler : RequestHandler
{
protected override bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect)
{
// delegate all none local URL to OS default browser
if (!request.Url.StartsWith("file:") &&
!request.Url.StartsWith(LocalSchemeHandlerFactory.SchemeName))
{
Process.Start(request.Url);
return true;
}
return false;
}
}
But, when opening a PDF file, an additional URL is called: chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html (why?).
Following the logic above, this is handled by the OS which causes opening a window: "You'll need a new app to open this chrome-extension link".
After extending the if statement everything works fine:
if (!request.Url.StartsWith("chrome-extension") &&
!request.Url.StartsWith("file:") &&
!request.Url.StartsWith(LocalSchemeHandlerFactory.SchemeName))
I have some images in my ftp web space, and through this method I can view them on the screen, and above them I insert a Label.
var ImageUrl = "ftp://xxxxxxxxx.jpg";
//Download Image
byte[] ImgByte1 = WebClient.DownloadData(ImageUrl);
MemoryStream mStream1 = new MemoryStream(ImgByte1);
ObservableCollection<FraseClass> listFrasi = new ObservableCollection<FraseClass>
{
new FraseClass{Source=ImageSource.FromStream(() => mStream1)},
}
XAML
<Image
Source="{Binding Source}"/>
<Label
Text="Hello"/>
I am looking for a way to be able to save the image on the device with the text superimposed. I tried to search but couldn't find anything to fix my problem
I am looking for a way to be able to save the image on the device with the text superimposed. I tried to search but couldn't find anything to fix my problem
According to your description, you want to save image that downloadind from url into local storage, am I right?
If yes, I suggest you can use Xamarin.Forms DependencyService to do this.
Firstly, create an interface in shared code.
public interface IImageFile
{
void SaveImage(string name, byte[] data, string location = "temp");
}
Then implement the interface on Android platform. Don't forget to register the platform implementations.
[assembly: Dependency(typeof(ImageFile))]
namespace FormsSample.Droid
{
public class ImageFile : IImageFile
{
public void SaveImage(string name, byte[] data, string location = "temp")
{
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
documentsPath = System.IO.Path.Combine(documentsPath, "Orders", location);
Directory.CreateDirectory(documentsPath);
string filePath = System.IO.Path.Combine(documentsPath, name);
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
{
int length = data.Length;
fs.Write(data, 0, length);
}
}
}
}
Finally, resolving platform implementations with the DependencyService
private void btn1_Clicked(object sender, EventArgs e)
{
var ImageUrl = "ftp://xxxxxxxxx.jpg";
//Download Image
byte[] ImgByte1 = WebClient.DownloadData(ImageUrl);
DependencyService.Get<IImageFile>().SaveImage("ImageName.jpg", ImgByte1, "imagesFolder");
}
you need to add permission WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE in AndroidMainfeast.xml, then you also need to Runtime Permission Checks in Android 6.0. Using the following code in Mainactivity.cs to request permissions.
private void checkpermission()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) == (int)Permission.Granted)
{
// We have permission, go ahead and use the writeexternalstorage.
}
else
{
// writeexternalstorage permission is not granted. If necessary display rationale & request.
}
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) == (int)Permission.Granted)
{
// We have permission, go ahead and use the ReadExternalStorage.
}
else
{
// ReadExternalStorage permission is not granted. If necessary display rationale & request.
}
}
Detailed info about IOS platform, you can take a look:
How to download image and save it in local storage using Xamarin-Forms.?
Update:
If you want to add text watermark on image, you need to convert byte[] to bitmap to add text firstly.
Android.Graphics.Bitmap mutableBitmap;
Android.Graphics.Bitmap bitmap;
public void ConvertImage(byte[] imageArray)
{
bitmap = BitmapFactory.DecodeByteArray(imageArray, 0, imageArray.Length);
}
public void Watermark()
{
mutableBitmap = bitmap.Copy(Android.Graphics.Bitmap.Config.Argb8888, true);
Canvas canvas = new Canvas(mutableBitmap);
// Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.Color = Android.Graphics.Color.Black;
paint.TextSize = 50;
canvas.DrawText("hellod world", 50, 50, paint);
}
Then convert bitmap into byte[] to save image into local storage.
public void convertbitmap(Bitmap bitmap)
{
bitmap = mutableBitmap;
MemoryStream stream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
byte[] bitmapData = stream.ToArray();
}
I'm trying to display a local pdf file using a custom LocalSchemeHandler which reads a memory stream from the file.
The file exists and the memory stream is being read properly. But there is nothing being displayed in the browser window. Displaying the same file via file scheme works.
ResourceHandler:
public class LocalSchemeHandler : ResourceHandler
{
public override bool ProcessRequestAsync(IRequest request, ICallback callback)
{
var uri = new Uri(request.Url);
var file = uri.AbsolutePath;
Task.Run(() =>
{
using (callback)
{
if (!File.Exists(file))
{
callback.Cancel();
return;
}
byte[] bytes = File.ReadAllBytes(file);
var stream = new MemoryStream(bytes);
if (stream == null)
{
callback.Cancel();
}
else
{
stream.Position = 0;
ResponseLength = stream.Length;
var fileExtension = Path.GetExtension(file);
MimeType = GetMimeType(fileExtension);
StatusCode = (int)HttpStatusCode.OK;
Stream = stream;
callback.Continue();
}
}
});
return true;
}
}
ISchemeHandlerFactory:
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "local";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new LocalSchemeHandler();
}
}
Settings:
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});
// Increase the log severity so CEF outputs detailed information, useful for debugging
settings.LogSeverity = LogSeverity.Default;
Cef.Initialize(settings);
EDIT
Trying to display the PDF file via ResourceHandler.FromFilePath also doesn't work (nothing is displayed).
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "local";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
var uri = new Uri(request.Url);
var file = uri.AbsolutePath;
var fileExtension = Path.GetExtension(file);
var mimeType = ResourceHandler.GetMimeType(fileExtension);
return ResourceHandler.FromFilePath(file, mimeType);
}
}
EDIT2
After setting LogSeverity to Default the log says: [0524/150955.108:INFO:CONSOLE(20)] "Refused to load plugin data from 'local://c/Users/abidh/Desktop/pdf.pdf' because it violates the following Content Security Policy directive: "object-src * blob: externalfile: file: filesystem: data:".
Didn't find a solution using google. Thanks to amaitland, using the IsCSPBypassing property solved the problem:
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory(),
IsCSPBypassing = true
});
settings.LogSeverity = LogSeverity.Error;
Cef.Initialize(settings);
Set the PDF file path to ChromiumWebBrowser.Address as format like file:///C:/Users/xxx/yyy.pdf, the CEFSharp will render PDF just like Chrome browser.
So, I have these two methods, which I am using to serialize and deserialize Images:
private static Image GetImageFromString(string image)
{
using (var stream = new MemoryStream(Convert.FromBase64String(image)))
{
return Image.FromStream(stream);
}
}
private static string GetImageAsString(Image image)
{
using (var stream = new MemoryStream())
{
image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
return Convert.ToBase64String(stream.GetBuffer());
}
}
If I do something Like this:
public Form1()
{
InitializeComponent();
var image = Image.FromFile(#"F:\phpide.png");
pictureBox1.Image = image;
var serialized = GetImageAsString(image);
var secondImage = GetImageFromString(serialized);
pictureBox2.Image = secondImage;
}
It works as expected
Although, If I do something like this:
//client
public void GetImage(JObject o)
{
var imageFile = o["file"].ToString();
if (!File.Exists(imageFile))
{
SendMessage("File does not exist");
return;
}
using (var image = Image.FromFile(imageFile))
{
var serialized = GetImageAsString(image);
var ob = new JObject
{
{ COMMAND, (int) Command.GetImage },
{ "content", serialized }
};
Send(ob);
ob = null;
serialized = null;
}
}
//server
public void ReceiveImage(JObject o)
{
var content = o["content"].ToString();
var image = GetImageFromString(content);
var form = new ImagePreviewForm(image);
form.Show();
}
//server
public ImagePreviewForm(Image image)
{
InitializeComponent();
pictureBox1.Image = image;
}
The image is just blank.
I have checked and the image is being received correctly, with no data loss.
What could be going wrong here? Where should I look?
This is at least one problem:
return Convert.ToBase64String(stream.GetBuffer());
You shouldn't use MemoryStream.GetBuffer here - you should use ToArray. The GetBuffer method returns the underlying buffer as-is... complete with junk data at the end of the buffer, beyond the logical current length of the stream.
Additionally, you shouldn't close the stream when you call Image.FromStream. From the docs:
You must keep the stream open for the lifetime of the Image.
So get rid of the using statement in GetImageFromString.
With the using statement you are disposing the image before the UI thread can display the image properly. Take the code out of the using block and add a Dispose() statement to the Form.Close() method.