Return multiple files in Get request - c#

I am using asp net web api. I need to create an update request to my client. Example i need my client to retreive all necessery files for making an update om his application.
So i have create a get method witch converts all files from server to bytes, then i am place them inside a list and i send it to my client. There are 15 total files. Total size is 150mbytes and after compression is 70mbytes.
public class DataFiles
{
public string FileName { get; set; }
public byte[] Data { get; set; }
}
public IHttpActionResult Get(string version)
{
var mItems = new List<DataFiles>();
var path = "C:\\Versions\\" + version;
if (Directory.Exists(path))
{
DirectoryInfo d = new DirectoryInfo(path);//Assuming Test is your Folder
FileInfo[] Files = d.GetFiles("*"); //Getting all files
foreach (FileInfo file in Files)
{
mItems.Add(new DataFiles
{
FileName = file.Name,
Data = Compress(File.ReadAllBytes(file.FullName))
});
}
}
return Ok(mItems);
}
public static byte[] Compress(byte[] data)
{
var output = new MemoryStream();
using (var dstream = new DeflateStream(output, CompressionLevel.Optimal))
{
dstream.Write(data, 0, data.Length);
}
return output.ToArray();
}
Is this method right? Shoud i send each file one by one? Should i use memorystream?

Related

Problems with returning zip file

I am writing an ASP.NET Core Web API with .NET 5.0 as an exercise.
In MyController.cs there is the method DownloadZip(). Here, it should be possible for the client to download a zip file. By the way, I create a zip file because I did not achieve to transfer multiple pictures. That is the actual goal. Provisionally, the zip file is still stored in the picture folder. Of course, that should not happen either. I simply still have difficulties with web services and transferring zip files via them.
Anyway, in the line return File(fullName, "text/plain"); I get the following error message:
System.InvalidOperationException: No file provider has been configured to process the supplied file.
I found several threads on StackOverflow last Friday about how to transfer a zip file using a memory stream. When I do it this way, the browser shows the individual bytes, but no finished file has been downloaded.
Postings is a list(of post) with
using System;
using System.Collections.Generic;
namespace ImageRepository
{
public sealed class Posting
{
public DateTime CreationTime { get; set; }
public List<ImageProperties> Imageproperties { get; }
public Posting(DateTime creationTime, List<ImageProperties> imPr)
{
CreationTime = creationTime;
Imageproperties = imPr;
}
}
}
And Imageproperties is the following:
namespace ImageRepository
{
public sealed class ImageProperties
{
public string FullName { get; set; }
public string _Name { get; set; }
public byte[] DataBytes { get; set; }
public ImageProperties(string FullName, string Name, byte[] dataBytes)
{
this.FullName = FullName;
this._Name = Name;
this.DataBytes = dataBytes;
}
}
}
MyController.cs
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using ImageRepository;
using System.IO.Compression;
namespace WebApp2.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
private readonly IImageTransferRepository imageRepository;
private readonly System.Globalization.CultureInfo Deu = new System.Globalization.CultureInfo("de-DE");
public MyController(IImageTransferRepository imageTransferRepository)
{
this.imageRepository = imageTransferRepository;
}
//––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
[HttpGet("WhatAreTheNamesOfTheLatestPictures")] // Route will be https://localhost:44355/api/My/WhatAreTheNamesOfTheLatestPictures/
public ActionResult GetNamesOfNewestPosting()
{
List<string> imageNames = this.imageRepository.GetImageNames();
if (imageNames.Count == 0)
{
return NoContent();
}
return Ok(imageNames);
}
//––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
[HttpGet("ImagesOfLatestPost")] //route will be https://localhost:44355/api/My/ImagesOfLatestPost
public ActionResult DownloadZip()
{
List<Posting> Postings = this.imageRepository.GetImages();
if (Postings is null || Postings.Count == 0)
{
return NoContent();
}
System.DateTime now = System.DateTime.Now;
string now_as_string = now.ToString("G", Deu).Replace(':', '-');
string folderPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
string fullName = $"{folderPath}\\{now_as_string}.zip";
using (ZipArchive newFile = ZipFile.Open(fullName, ZipArchiveMode.Create))
{
for (int i = 0; i < Postings[0].Imageproperties.Count; i++)
{
newFile.CreateEntryFromFile(Postings[0].Imageproperties[i].FullName,
Postings[0].Imageproperties[i]._Name);
}
}
return File(fullName, "text/plain");
}
}
}
Edit June 20, 2022, 4:16 pm
Based on Bagus Tesa's comment, I wrote the following:
byte[] zip_as_ByteArray = System.IO.File.ReadAllBytes(fullName);
return File(zip_as_ByteArray, "application/zip");
The automatic download takes place, but I still have to rename the file by attaching (a) .zip so that Windows recognises it as a zip file.
Furthermore, there is still the problem that I am still creating the zip file on the hard disk (using (ZipArchive newFile = ZipFile.Open(fullName, ZipArchiveMode.Create))). How can I change this?
Thanks to the thread linked by Bagus Tesa, I can now answer my question. I have adapted a few things to my needs, see for-loop, because I have several images.
[HttpGet("ImagesOfLatestPost")] //route will be https://localhost:44355/api/My/ImagesOfLatestPost
public ActionResult DownloadZip()
{
List<Posting> Postings = this.imageRepository.GetImages();
if (Postings is null || Postings.Count == 0)
{
return NoContent();
}
byte[] compressedBytes;
using (var outStream = new System.IO.MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
for (int i = 0; i < Postings[0].Imageproperties.Count; i++)
{
ZipArchiveEntry fileInArchive = archive.CreateEntry(Postings[0].Imageproperties[i]._Name, CompressionLevel.Optimal);
using System.IO.Stream entryStream = fileInArchive.Open();
using System.IO.MemoryStream fileToCompressStream = new System.IO.MemoryStream(Postings[0].Imageproperties[i].DataBytes);
fileToCompressStream.CopyTo(entryStream);
}
}
compressedBytes = outStream.ToArray();
}
return File(compressedBytes, "application/zip", $"Export_{System.DateTime.Now:yyyyMMddhhmmss}.zip");
}

Parse FormData [] with array of files - to Controller with saving to PSQL

Good day,
There is MVC backend - How solution should be designed for this type of problem?
So FE send this array of files:
FormData:[
file0: (binary)
file1: (binary)
file2: (binary)
]
Then on BE Side there is Controller:
Currently solutions store those files to filesystem.
[AllowAnonymous]
[HttpPost("file")]
public async Task<ActionResult> PostAsync([FromForm] List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
Console.WriteLine(files);
Console.WriteLine(size);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", formFile.FileName);
using var stream = System.IO.File.Create(filePath);
await formFile.CopyToAsync(stream);
}
}
// Process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { count = files.Count, size });
}
There is a model with that Files (it should have more properties in future)
namespace WebApi.Models
{
public class Model
{
public string Name { get; set; }
public byte[] Files { get; set; }
}
}
The question is: How map this array of binary object to Example model and store this Materials data in database(postgress) ?
It sounds like your problem can be distilled down to two different questions:
How do I get a byte[] from IFormFile?
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
using var memoryStream = new MemoryStream();
await formFile.CopyToAsync(memoryStream);
var model = new Model
{
Name = formFile.Name,
Files = memoryStream.ToArray()
};
// add model to database
}
}
How do I save a byte[] in PostgreSQL?
Using Npgsql, you can directly map a byte[] in C# to a bytea column in PostgreSQL.

c# Send image from WPF to WebAPI

I have a WebAPI 2.1 service (ASP.Net MVC 4) that receive and image and related data.
I need to send this image from WPF application, but I get 404 not found error.
Server side
[HttpPost]
[Route("api/StoreImage")]
public string StoreImage(string id, string tr, string image)
{
// Store image on server...
return "OK";
}
Client side
public bool SendData(decimal id, int time, byte[] image)
{
string url = "http://localhost:12345/api/StoreImage";
var wc = new WebClient();
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
var parameters = new NameValueCollection()
{
{ "id", id.ToString() },
{ "tr", time.ToString() },
{ "image", Convert.ToBase64String(image) }
};
var res=wc.UploadValues(url, "POST", parameters);
return true;
}
The url exists, I thing I need to encode to json format, but I don't know how.
Thanks for your time!
The method parameters in your case are received in QueryString form.
I would suggest you turn the parameters list into one single object like this:
public class PhotoUploadRequest
{
public string id;
public string tr;
public string image;
}
Then in you API convert the string to buffer from Base64String like this:
var buffer = Convert.FromBase64String(request.image);
Then cast it to HttpPostedFileBase
HttpPostedFileBase objFile = (HttpPostedFileBase)new MemoryPostedFile(buffer);
Now you have the image file. Do whatever you want.
Full Code here:
[HttpPost]
[Route("api/StoreImage")]
public string StoreImage(PhotoUploadRequest request)
{
var buffer = Convert.FromBase64String(request.image);
HttpPostedFileBase objFile = (HttpPostedFileBase)new MemoryPostedFile(buffer);
//Do whatever you want with filename and its binaray data.
try
{
if (objFile != null && objFile.ContentLength > 0)
{
string path = "Set your desired path and file name";
objFile.SaveAs(path);
//Don't Forget to save path to DB
}
}
catch (Exception ex)
{
//HANDLE EXCEPTION
}
return "OK";
}
Edit:
I forgot to add the Code for MemoryPostedFile class
public class MemoryPostedFile : HttpPostedFileBase
{
private readonly byte[] fileBytes;
public MemoryPostedFile(byte[] fileBytes, string fileName = null)
{
this.fileBytes = fileBytes;
this.FileName = fileName;
this.InputStream = new MemoryStream(fileBytes);
}
public override void SaveAs(string filename)
{
File.WriteAllBytes(filename, fileBytes);
}
public override string ContentType => base.ContentType;
public override int ContentLength => fileBytes.Length;
public override string FileName { get; }
public override Stream InputStream { get; }
}

Read an Excel spreadsheet in memory

How can I read an Excel spreadsheet that was just posted to my server?
I searched for something but I only found how to read an Excel spreadsheet with the file name path which is not my case.
I need something like that:
public ActionResult Import(HttpPostedFileBase file)
{
var excel = new ExcelQueryFactory(file); //using linq to excel
}
I was running into your same issue but I didn't want to switch to a paid service so this is what I did.
public class DataImportHelper : IDisposable
{
private readonly string _fileName;
private readonly string _tempFilePath;
public DataImportHelper(HttpPostedFileBase file, string tempFilePath)
{
_fileName = file.FileName;
_tempFilePath = Path.Combine(tempFilePath, _fileName);
(new FileInfo(_tempFilePath)).Directory.Create();
file.SaveAs(_tempFilePath);
}
public IQueryable<T> All<T>(string sheetName = "")
{
if (string.IsNullOrEmpty(sheetName))
{
sheetName = (typeof (T)).Name;
}
var excelSheet = new ExcelQueryFactory(_tempFilePath);
return from t in excelSheet.Worksheet<T>(sheetName)
select t;
}
public void Dispose()
{
File.Delete(_tempFilePath);
}
}
Here is a Test
[Fact]
public void AcceptsAMemoryStream()
{
MemoryFile file;
using (var f = File.OpenRead("SampleData.xlsx"))
{
file = new MemoryFile(f, "multipart/form-data", "SampleData.xlsx");
using (var importer = new DataImportHelper(file, "Temp/"))
{
var products = importer.All<Product>();
Assert.NotEmpty(products);
}
}
}
Here is MemoryFile.cs. This file is only used for testing. It is just an implementation of HttpPostedFileBase so you can test your controllers and my little helper. This was borrowed from another post.
public class MemoryFile : HttpPostedFileBase
{
Stream stream;
string contentType;
string fileName;
public MemoryFile(Stream stream, string contentType, string fileName)
{
this.stream = stream;
this.contentType = contentType;
this.fileName = fileName;
}
public override int ContentLength
{
get { return (int)stream.Length; }
}
public override string ContentType
{
get { return contentType; }
}
public override string FileName
{
get { return fileName; }
}
public override Stream InputStream
{
get { return stream; }
}
public override void SaveAs(string filename)
{
using (var file = File.Open(filename, FileMode.Create))
stream.CopyTo(file);
}
}
Unfortunately it's not possible to read a spreadsheet from a stream with LinqToExcel.
That's because it uses OLEDB to read from the spreadsheets and it can't read from a stream.
You can use the InputStream property of HttpPostedFileBase to read the excel spreadsheet in memory.
I use ClosedXML nuget package to read excel content from stream which is available in your case. It has a simple overload which takes stream pointing to stream for the excel file (aka workbook).
imported namespaces at the top of the code file:
using ClosedXML.Excel;
Source code:
public ActionResult Import(HttpPostedFileBase file)
{
//HttpPostedFileBase directly is of no use so commented your code
//var excel = new ExcelQueryFactory(file); //using linq to excel
var stream = file.InputStream;
if (stream.Length != 0)
{
//handle the stream here
using (XLWorkbook excelWorkbook = new XLWorkbook(stream))
{
var name = excelWorkbook.Worksheet(1).Name;
//do more things whatever you like as you now have a handle to the entire workbook.
var firstRow = excelWorkbook.Worksheet(1).Row(1);
}
}
}
You need Office Interops assemblies. Check the Excel Object Model for reference.

WordPress XML RPC Upload Image C#

Hey all, I am developing a site for work that will push info from a database into Wordpress using Wordpress XML RPC. I can grab info and post it just fine, however when I get to the point of uploading images it seems to work(no runtime errors/image in WP Media Tab) however it uploads a broken image link. It appears it is somehow no getting the data from my image and I am not certain why here is some of my code.
MemoryStream ms = new MemoryStream();
System.Drawing.Image img = System.Drawing.Image.FromFile(HttpContext.Current.Server.MapPath("_Images/DownloadButton-PSD.png"));
img.Save(ms, ImageFormat.Png);
byte[] imagebytes = new byte[ms.Length];
ms.Position = 0;
ms.Read(imagebytes, 0, Convert.ToInt32(ms.Length));
after that code loads the image info I pass it to the function in the format of a Data variable
var data = new Data
{
Base64 = Convert.ToBase64String(imagebytes),
Name = "DownloadButton-PSD.png",
Type = "image/png",
Overwrite = false,
};
_wpWrapper.UploadFile(data);
FYI: I am also using the dll's from
http://joeblogs.codeplex.com/
for my project
The Data Class looks like this:
public class Data
{
public string Name { get; set; }
public string Type { get; set; }
public string Base64 { get; set; }
public bool Overwrite { get; set; }
}
The Upload File Function looks like this:
public void UploadFile(Data data)
{
var xmlRpcData = Map.From.Data(data);
var result = _wrapper.UploadFile(this.BlogID, Username, Password, xmlRpcData);
}
In JoeBlogs library try using the class MetaWeblogWrapper and method: MediaObjectInfo NewMediaObject(MediaObject mediaObject) - for upload image.

Categories