c# - vectorial - Archivo libre bloqueado por un nuevo mapa de bits(filePath)
tipos de archivos de imagenes de mapa de bits (9)
Tengo la imagen de un PictureBox apuntando a un cierto archivo "A", en el tiempo de ejecución quiero cambiar la imagen del PictureBox a una "B" diferente, pero me sale el siguiente error:
"Se produjo una excepción de primera oportunidad del tipo ''System.IO.IOException'' en mscorlib.dll Información adicional: el proceso no puede acceder al archivo" A "porque lo está utilizando otro proceso."
Estoy configurando la imagen de la siguiente manera:
pbAvatar.Image = new Bitmap(filePath);
¿Cómo puedo desbloquear el primer archivo?
¡Gracias por adelantado!
Aquí está la técnica que estoy usando actualmente, y parece funcionar mejor. Tiene la ventaja de producir un objeto Bitmap con el mismo formato de píxel (24 bits o 32 bits) y resolución (72 ppp, 96 ppp, lo que sea) como archivo de origen.
// ImageConverter object used to convert JPEG byte arrays into Image objects. This is static
// and only gets instantiated once.
private static readonly ImageConverter _imageConverter = new ImageConverter();
Esto puede usarse tantas veces como sea necesario, de la siguiente manera:
Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));
Editar: Aquí hay una actualización de la técnica anterior: https://.com/a/16576471/253938
Aquí está mi enfoque para abrir una imagen sin bloquear el archivo ...
public static Image FromFile(string path)
{
var bytes = File.ReadAllBytes(path);
var ms = new MemoryStream(bytes);
var img = Image.FromStream(ms);
return img;
}
ACTUALIZACIÓN: Hice algunas pruebas de rendimiento para ver qué método era el más rápido. Lo comparé con la respuesta @net_progs "copiar desde mapa de bits" (que parece ser la más cercana para corregir, aunque tiene algunos problemas). Cargué la imagen 10000 veces para cada método y calculé el tiempo promedio por imagen. Aquí están los resultados:
Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
Los resultados parecen tener sentido ya que tiene que crear la imagen dos veces usando la copia del método de mapa de bits.
El uso de una secuencia de archivos desbloqueará el archivo una vez que se haya leído y desechado:
using (var fs = new System.IO.FileStream("c://path to file.bmp", System.IO.FileMode.Open))
{
var bmp = new Bitmap(fs);
pct.Image = (Bitmap) bmp.Clone();
}
Editar: se actualizó para permitir que se elimine el mapa de bits original y permite que FileStream se cierre.
ESTA RESPUESTA NO ES SEGURA - Ver comentarios y ver la discusión en la respuesta de net_prog . Editar para usar Clone
no lo hace más seguro: Clone clona todos los campos, incluida la referencia de la ruta de archivos, que en ciertas circunstancias causará un problema.
Esta es una pregunta de bloqueo común ampliamente debatida en la web.
El truco sugerido con la transmisión no funcionará , en realidad funciona inicialmente, pero causa problemas más adelante. Por ejemplo, cargará la imagen y el archivo permanecerá desbloqueado, pero si intenta guardar la imagen cargada mediante el método Save (), generará una excepción GDI + genérica.
A continuación, el camino con la replicación por píxel no parece ser sólido, al menos es ruidoso.
Lo que encontré trabajando se describe aquí: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx
Así es como debe cargarse la imagen:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
Estaba buscando una solución a este problema y este método funciona bien para mí hasta ahora, así que decidí describirlo, ya que descubrí que muchas personas aconsejan el enfoque de flujo incorrecto aquí y en la web.
Hace tres años, escribí un programa de visor de imágenes solo para ver si podía hacerlo. La semana pasada añadí código para escanear imágenes. (Planeo agregar esto a un programa de genealogía después de que saque los errores). Para recortar el área no utilizada, haré que el programa llame a MSPaint con el nombre del archivo. Lo edito allí y luego lo guardo. Cuando cierro Paint, la imagen muestra los cambios.
Estaba obteniendo un error en Paint sobre el bloqueo del archivo si hacía algo con la imagen. Cambio el programa para bloquear una imagen, usando Image, FromStream (). Ya no recibo ese mensaje en Paint. (Mi programa está en VB 2010.)
Hasta donde yo sé, esto es 100% seguro, ya que la imagen resultante se crea al 100% en la memoria, sin ningún recurso vinculado, y sin corrientes abiertas en la memoria. Actúa como cualquier otro Bitmap
creado a partir de un constructor que no especifica ninguna fuente de entrada y, a diferencia de otras respuestas aquí, conserva el formato de píxeles original, lo que significa que puede usarse en formatos indexados.
En función de esta respuesta , pero con correcciones adicionales y sin importar la biblioteca externa.
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
Int32 h = sourceImage.Height;
Int32 origStride = sourceData.Stride;
Int32 targetStride = targetData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
IntPtr sourcePos = sourceData.Scan0;
IntPtr destPos = targetData.Scan0;
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; y++)
{
Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
Marshal.Copy(imageData, 0, destPos, actualDataWidth);
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
targetImage.UnlockBits(targetData);
sourceImage.UnlockBits(sourceData);
// For indexed images, restore the palette. This is not linking to a referenced
// object in the original image; the getter of Palette creates a new object when called.
if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
targetImage.Palette = sourceImage.Palette;
// Restore DPI settings
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
return targetImage;
}
Para llamar, simplemente use:
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
O bien, desde bytes:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
Léelo en la secuencia, crea un mapa de bits y cierra la transmisión.
No puede deshacer / cerrar una secuencia mientras un objeto de mapa de bits todavía la está usando. (Si el objeto de mapa de bits necesitará acceder de nuevo solo es determinista si sabe con qué tipo de archivo está trabajando y exactamente qué operaciones va a realizar. Por ejemplo, para ALGUNAS imágenes en formato .gif, la transmisión se cierra antes el constructor regresa)
Clone crea una "copia exacta" del mapa de bits (por documentación; ILSpy lo muestra llamando a métodos nativos, por lo que es demasiado para rastrear en este momento), también copia esos datos de Stream, o de lo contrario no sería un copia exacta.
Su mejor opción es crear una réplica perfecta de píxeles de la imagen, aunque YMMV (con ciertos tipos de imágenes puede haber más de un cuadro, o puede que tenga que copiar datos de paleta también). Pero para la mayoría de las imágenes, esto funciona :
static Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap b = new Bitmap(stream))
{
retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
using (Graphics g = Graphics.FromImage(retval))
{
g.DrawImage(b, Point.Empty);
g.Flush();
}
}
return retval;
}
Y luego puedes invocarlo así:
using (Stream s = ...)
{
Bitmap x = LoadImage(s);
}
( La respuesta aceptada es incorrecta. Cuando intente LockBits(...)
en el mapa de bits clonado, eventualmente encontrará errores GDI +).
- Copie su archivo a un archivo temporal y ábralo de la manera más fácil
new Bitmap(temp_filename)
- abra su archivo, lea la imagen, cree una copia de formato pixel-size-pixel (no
Clone()
) y elimine el primer mapa de bits - (acepta la característica de archivo bloqueado)