How to convert GZipStream to HttpContent? - c#

I have written a Web API code to return a zip file. But I am not able to convert the GZipStream content to HttpContent. I get the following error:
cannot implicitly convert type 'system.io.compression.GZipStream' to
'System.Net.Http.HttpContent'
Where did I go wrong?
My WebApi Code:
var content =
new GZipStream(memStream, CompressionMode.Compress);
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = content;
httpResponseMessage.Content.Headers.Add("x-filename", document.FileName);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = "xyz.zip";
httpResponseMessage.StatusCode = HttpStatusCode.OK;

I got a solution using Zip archive and I have created a static class to push the memory stream into a zip archive as below,
var pushStreamContent = ZipStreamContent.Create("MultipleDocument.zip", memStList);
ZipStreamContent class,
public static class ZipStreamContent
{
public static PushStreamContent Create(string fileName, List<MemoryStream> msList)
{
var content = new PushStreamContent((outputStream, httpContent, transportContext) =>
{
using (var zip = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: false))
{
msList[0].Position = 0;
var createenter = zip.CreateEntry("xyz.jpg", CompressionLevel.Optimal);
using (var s = createenter.Open())
{
msList[0].CopyTo(s);
}
}
});
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
content.Headers.ContentDisposition.FileName = fileName;
return content;
}
}

I have simplified the above given GipStreamContent static class like following, It's working well, so I hope It will help all others.
CloudBlockBlob blob = null;
//azure storage connection
var container = GetBlobClient(tenantInfo);
//directory reference
var directory = container.GetDirectoryReference(
string.Format(DirectoryNameConfigValue, tenantInfo.TenantId.ToString(), documentList[0].ProjectId));
var pushStreamContent = new PushStreamContent(async (outputStream, httpContent, transportContext) =>
{
//zip the multiple files
using (var zipEntry = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: false))
{
for (int docId = 0; docId < documentList.Count; docId++)
{
blob = directory.GetBlockBlobReference(DocumentNameConfigValue + documentList[docId].DocumentId);
if (!blob.Exists()) continue;
MemoryStream memStream = new MemoryStream();
await blob.DownloadToStreamAsync(memStream);
memStream.Position = 0;
var createEntry = zipEntry.CreateEntry(documentList[docId].FileName, CompressionLevel.Fastest);
using (var stream = createEntry.Open())
{
memStream.CopyTo(stream);
}
}
}
});

Related

DotNetZip creates 0kb files when passing in memory stream

I have a Razor page which I want to generate a Zip file containing multiple CSV files.
It works fine when I just want to generate one file, e.g.
public async Task<FileStreamResult> OnGet(int id)
{
var bankDetails = _paymentFileGenerator.GeneratePaymentFiles(id);
await using var memoryStream = new MemoryStream();
await using var streamWriter = new StreamWriter(memoryStream);
await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture)
{
Configuration = { HasHeaderRecord = false, }
};
csvWriter.WriteRecords(bankDetails);
streamWriter.Flush();
return new FileStreamResult(new MemoryStream(memoryStream.ToArray()), new MediaTypeHeaderValue("text/csv"))
{
FileDownloadName = "bacs.csv"
};
}
But when I try to pass memory streams for two files into a DotNetZip stream the zip downloads to the browser but both files are 0kb. Any thoughts on why?
public async Task<FileStreamResult> OnGet(int id)
{
var bankFiles = _paymentFileGenerator.GeneratePaymentFiles(id);
using var zipStream = new MemoryStream();
using var zip = new ZipFile();
await using var bankFileStream = new MemoryStream();
await using var bankFileStreamWriter = new StreamWriter(bankFileStream);
await using var bankFileCsvWriter = new CsvWriter(bankFileStreamWriter, CultureInfo.InvariantCulture)
{
Configuration = { HasHeaderRecord = false, }
};
bankFileCsvWriter.WriteRecords(bankFiles.BankFile);
bankFileCsvWriter.Flush();
bankFileStream.Seek(0, SeekOrigin.Begin);
zip.AddEntry("bacs.csv", (name, stream) => bankFileStream.ToArray());
await using var internalFileStream = new MemoryStream();
await using var internalFileStreamWriter = new StreamWriter(internalFileStream);
await using var internalFileCsvWriter = new CsvWriter(internalFileStreamWriter, CultureInfo.InvariantCulture);
internalFileCsvWriter.WriteRecords(bankFiles.InternalFile);
internalFileCsvWriter.Flush();
internalFileStream.Seek(0, SeekOrigin.Begin);
zip.AddEntry("internal.csv", (name, stream) => internalFileStream.ToArray());
zip.Save(zipStream);
zipStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(new MemoryStream(zipStream.ToArray()), new MediaTypeHeaderValue("application/zip"))
{
FileDownloadName = "paymentbatch.zip"
};
}
I've seen other StackOverflow posts where people suggested adding the Seek() function to reset the position of the streams but it didn't work for me whether that was there or not.
When debugging, I can see that the 'bankfileStream' stream has bytes in it when I call the zip.AddEntry() but then the zipStream shows 0 bytes when I call zip.Save(zipStream).
Any suggestions appreciated!
I tried many different options and nothing worked until I used the SharpZipLib library instead. Here is the full solution:
public async Task<FileStreamResult> OnGet(int id)
{
var bankFiles = _paymentFileGenerator.GeneratePaymentFiles(id);
var bankFileBytes = await GetCsvFileBytes(bankFiles.BankFile, includeHeader: false);
var internalFileBytes = await GetCsvFileBytes(bankFiles.InternalFile);
var files = new List<AttachedFile>
{
new AttachedFile("bacs.csv", bankFileBytes),
new AttachedFile("internal.csv", internalFileBytes)
};
var zipStream = AddFilesToZip(files);
return new FileStreamResult(zipStream, new MediaTypeHeaderValue("application/zip"))
{
FileDownloadName = "paymentbatch.zip"
};
}
public MemoryStream AddFilesToZip(List<AttachedFile> attachedFiles)
{
var outputMemStream = new MemoryStream();
using (var zipStream = new ZipOutputStream(outputMemStream))
{
// 0-9, 9 being the highest level of compression
zipStream.SetLevel(3);
foreach (var file in attachedFiles)
{
var newEntry = new ZipEntry(file.Name) {DateTime = DateTime.Now};
zipStream.PutNextEntry(newEntry);
StreamUtils.Copy(new MemoryStream(file.Bytes), zipStream, new byte[4096]);
}
zipStream.CloseEntry();
// Stop ZipStream.Dispose() from also Closing the underlying stream.
zipStream.IsStreamOwner = false;
}
outputMemStream.Position = 0;
return outputMemStream;
}
private static async Task<byte[]> GetCsvFileBytes<T>(List<T> records, bool includeHeader = true) where T : class
{
await using var bankFileStream = new MemoryStream();
await using var bankFileStreamWriter = new StreamWriter(bankFileStream);
await using var bankFileCsvWriter = new CsvWriter(bankFileStreamWriter, CultureInfo.InvariantCulture)
{
Configuration = {HasHeaderRecord = includeHeader}
};
bankFileCsvWriter.WriteRecords(records);
bankFileStreamWriter.Flush();
return bankFileStream.ToArray();
}
public class AttachedFile
{
public byte[] Bytes { get; set; }
public string Name { get; set; }
public AttachedFile(string name, byte[] bytes)
{
Bytes = bytes;
Name = name;
}
}

zip multiple pdfs from url link, how to

I have a project that requires pdf files to be zipped up from an URL link and then downloaded and clickable by the end users browser. So far, I was able to zip one pdf file which isn't nearly what I'm looking to for.
I'm not sure how to proceed from here. Below is the code. Any help would be very much appreciated.
ASP.NET Core
[HttpGet("zipFiles")]
public IActionResult ZipPDFFiles()
{
var fileNames = _repo.GetFileNames();
foreach (var filesName in fileNames)
{
var urlLink = "https://example.com/folder/" + $"{filesName.PdfFileName}";
var net = new System.Net.WebClient();
var data = net.DownloadData(urlLink);
var file = $"{filesName.PdfFileName}";
var contentType = "application/zip";
string zippedFolderName = "Archive.zip";
using (MemoryStream ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
var zipArchiveEntry = archive.CreateEntry($"{file}", System.IO.Compression.CompressionLevel.Fastest);
using (var zipStream = zipArchiveEntry.Open()) zipStream.Write(data, 0, data.Length);
}
return File(ms.ToArray(), contentType, $"{zippedFolderName}");
}
}
return NotFound();
}
Some improvements:
used HttpClient
files uploaded simultaneously
var fileUrls = new[]
{
new Uri("https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/media/image3.5.png"),
new Uri("https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/media/image4.png"),
new Uri("https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/media/image6.png")
};
var downloadResults = fileUrls
.Select(uri => (uri: uri, response: HttpClientFactory.Create().SendAsync(new HttpRequestMessage(HttpMethod.Get, uri))))
.ToArray();
await Task.WhenAll(downloadResults.Select(v => v.response));
using (var ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var download in downloadResults)
{
var entry = archive.CreateEntry(download.uri.Segments.Last(), CompressionLevel.Fastest);
using (var zipStream = entry.Open())
{
var data = await download.response.Result.Content.ReadAsByteArrayAsync();
zipStream.Write(data, 0, data.Length);
}
}
}
return File(ms.ToArray(), contentType, $"{zippedFolderName}");
}

How can i send a MultipartFormDataContent array to an api using c#

I want to send a multipartformDataContent array to an api.
MultipartFormDataContent[] content = new MultipartFormDataContent[Array.Length];
for (int i = 0; i < Array.Length; i++)
{
foreach (var para in Array[i])
{
if (para.Key == "file")
{
FileParameter fileToUpload = (FileParameter)para.Value;
var stream = new MemoryStream(fileToUpload.File);
content[i].Add(new StreamContent(stream), para.Key, fileToUpload.FileName);
}
else
{
String String = para.Value.ToString();
content[i].Add(new StringContent(String), para.Key);
}
}
}
I want to post this multipartformdataContent array to an api. Is it possible?
Here is what's needed to combine FileContent with other data into MultipartFormDataContent:
using (var memoryStream = new MemoryStream())
{
// Copy your file into memoryStream
memoryStream.Position = 0;
var fileContent = new ByteArrayContent(memoryStream.ToArray());
var multiContent = new MultipartFormDataContent {{fileContent, "File", "Blah.pdf"}};
foreach (var pair in nameValues)
{
multiContent.Add(new StringContent(pair.Value), pair.Key);
}
return await httpClient.PostAsync(requestUri, multiContent);
}

How to generate a zip file within the HttpResponseMessage from within an Api Controller [duplicate]

I have a web service that I can call and save the returned csv file. Everything seems to be working OK. What I am now interested in doing is returning multiple CSV files for the user to download. What is the proper way to handle this? I'm guessing I need a way to package them up (zip? perhaps)?
[HttpPost]
[Route("OutputTemplate")]
public HttpResponseMessage OutputTemplate()
{
HttpResponseMessage msg = new HttpResponseMessage();
string body = this.Request.Content.ReadAsStringAsync().Result;
try
{
string contents = DoStuff(body) // get contents based on body
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(contents);
writer.Flush();
stream.Position = 0;
msg.StatusCode = HttpStatusCode.OK;
msg.Content = new StreamContent(stream);
msg.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
msg.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "fileexport"
};
return msg;
}
...
}
Using the following model to abstract file name and content
public class FileModel {
public string FileName { get; set; }
public byte[] FileContent { get; set; }
}
The following extension was derived to compress the file content
public static class ZipArchiveExtensions {
public static Stream Compress(this IEnumerable<FileModel> files) {
if (files.Any()) {
var ms = new MemoryStream();
using(var archive = new ZipArchive(
stream: ms,
mode: ZipArchiveMode.Create,
leaveOpen: true
)){
foreach (var file in files) {
var entry = archive.add(file);
}
}
ms.Position = 0;
return ms;
}
return null;
}
private static ZipArchiveEntry add(this ZipArchive archive, FileModel file) {
var entry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest);
using (var stream = entry.Open()) {
stream.Write(file.FileContent, 0, file.FileContent.Length);
}
return entry;
}
}
With that in place, the example API controller action could look something like this.
public class ExampleApiController : ApiController {
public async Task<IHttpActionResult> OutputTemplate() {
IHttpActionResult result = BadRequest();
var body = await Request.Content.ReadAsStreamAsync();
List<FileModel> files = DoSomething(body);
if (files.Count > 1) {
//compress the files.
var archiveStream = files.Compress();
var content = new StreamContent(archiveStream);
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
response.Content = content;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = "fileexport.zip"
};
result = ResponseMessage(response);
} else if (files.Count == 1) {
//return the single file
var fileName = files[0].FileName; //"fileexport.csv"
var content = new ByteArrayContent(files[0].FileContent);
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
response.Content = content;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = fileName
};
result = ResponseMessage(response);
}
return result;
}
private List<FileModel> DoSomething(System.IO.Stream body) {
//...TODO: implement file models
throw new NotImplementedException();
}
}

Return Zip file as Response in Web Api

Hi I have created a web api application and I am returning response as text file. Now I want to return a zipped file so that its size is reduced
This is my controller code
public HttpResponseMessage Get(String parameter)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(informationcontext.Records(parameter));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "sample"
};
return response;
}
Can any one help me in this?
public HttpResponseMessage Post()
{
var ch = new ClientHandler();
using (MemoryStream rms = new MemoryStream(Request.Content.ReadAsByteArrayAsync().Result))
{
using (GZipStream unzip = new GZipStream(rms, CompressionMode.Decompress))
{
ch.Requests = XElement.Load(unzip);
}
}
MemoryStream outstream = new MemoryStream();
try
{
ch.Start();
using (GZipStream zip = new GZipStream(outstream, CompressionMode.Compress, true))
{
ch.Responses.Save(zip);
}
}
catch (Exception ex)
{
XElement responses = new XElement("Responses");
responses.Add(new XElement("Error", ex.Message));
using (GZipStream zip = new GZipStream(outstream, CompressionMode.Compress, true))
{
responses.Save(zip);
}
}
finally
{
ch.Dispose();
}
outstream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(outstream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}

Categories