I'm trying to receive a file using Web Api from a user and then convert the file into a FileStream without writing the file to a server(Must stay in memory).
I have code that will allow me to write it to the server but all attempts to put it into a FileStream without writing the file to the server have failed.
public class ReceiverController : ApiController
{
public Task<HttpResponseMessage> Upload()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType));
}
string root = HttpContext.Current.Server.MapPath("~/App_Data/");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
FileInfo fileInfo = new FileInfo(provider.FileData.First().LocalFileName);
string fileName = provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "");
if (!File.Exists(Path.Combine(root, fileName)))
{
File.Move(fileInfo.FullName, Path.Combine(root, fileName));
}
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
});
return task;
}
}
fileName.PostedFile.InputStream --- gives me Stream and I use the following code to convert it to byte array. Then byte array is converted to filestream according to the following code.
BinaryReader br = new BinaryReader(fileName);
bytary= br.ReadBytes((Int32)fileName.Length);
for(int i = 0; i < bytary.Length; i++)
{
fileStream.WriteByte(bytary[i]);
}
Related
can I set a post method to post image and list of object in the same time I have the following code it's logically working but I'm trying to test this request with postman
private ApplicationDbContext1 db = new ApplicationDbContext1();
[Route("api/save_BUY")]
[HttpPost]
public async Task< IHttpActionResult> save_BUY(IEnumerable<BY_table> BY_table,int id)
{
var BY_API = BY_table.Select(p => new BY_API
{
ITEM_CODE = p.ITEM_CODE,
ITEM_NAME = p.ITEM_NAME,
Unit = p.Unit,
Unit_Core = Convert.ToDecimal(p.Unit_Core),
}).AsQueryable();
var ctx = HttpContext.Current;
var root = ctx.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.FileData)
{
var name = file.Headers
.ContentDisposition
.FileName;
var localFileName = file.LocalFileName;
var filePath = Path.Combine(
root, "files", name);
byte[] fileBytes;
using (var fs = new FileStream(
PHOTO, FileMode.Open, FileAccess.Read))
{
fileBytes = new byte[fs.Length];
fs.Read(
fileBytes, 0, Convert.ToInt32(fs.Length));
}
var MASTER_BUY = db.MASTER_BUY.Where(x => x.int==id)
{
MASTER_BUY.image= fileBytes;
};
db.SaveChanges();
return Ok(BY_API);
}
}
how can I make this request in postman to test it.
pleas help.
You Can send Image Form of base64 String And Get String value from Client(Postman)
Image to Base 64 String
This is one Of Online tools for help you get base 64 string from file!
I am sending a image file to wcf web service using retrofit, on saving side of wcf web service i am unable to save the stream file.
In android i creating like
//ApiInterface.class
#Multipart
#POST("RestService/json/PostUploadFile/")
Call<UploadFileResponse> uploadFile(#Part MultipartBody.Part file);
service call be like
File file = new File(assets.get(0).getPath());
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part part = MultipartBody.Part.createFormData("imageData", file.getName(), requestFile);
//api call method
callUploadFile(part, this);
private void callUploadFile(MultipartBody.Part part,
MainInteractor.OnFinishedListener listenerP) {
final MainInteractor.OnFinishedListener listener = listenerP;
HashMap<String, String> headerMap = new HashMap<>();
headerMap.put("SessionID", "");
headerMap.put("UserName", "");
OkHttpClient httpClient = ConnectToService.newInstance().addHeaders(getContext(), headerMap);
ApiInterface apiService =
ConnectToService.newInstance()
.getClient(httpClient).create(ApiInterface.class);
Call<UploadFileResponse> call = apiService.uploadFile(part);
call.enqueue(new Callback<UploadFileResponse>() {
#Override
public void onResponse(Call<UploadFileResponse> call, Response<UploadFileResponse> response) {
onFinished(response.body().getResult());
}
#Override
public void onFailure(Call<UploadFileResponse> call, Throwable t) {
if (t.getLocalizedMessage() != null) {
onFinishedFailure(t.getLocalizedMessage());
}
}
});
}
In wcf webservice i am getting data in message but when i save i get argument exception error.
EDITED: Below code works
Bitmap bm = BitmapFactory.decodeFile(assets.get(finalX).getPath());
Bitmap bitmap = Bitmap.createScaledBitmap(bm, 480, 480, true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); //bm is the bitmap object
byte[] byteArray = baos.toByteArray();
RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), byteArray);
callUploadFile(body, "File_" + finalX, MainFragment.this);
calling service method
private void callUploadFile(RequestBody body, String fileName,
MainInteractor.OnFinishedListener listenerP) {
final MainInteractor.OnFinishedListener listener = listenerP;
HashMap<String, String> headerMap = new HashMap<>();
headerMap.put("SessionID", "");
headerMap.put("UserName", "");
headerMap.put("FileName", fileName);
OkHttpClient httpClient = ConnectToService.newInstance().addHeaders(getContext(), headerMap);
ApiInterface apiService =
ConnectToService.newInstance()
.getClient(httpClient).create(ApiInterface.class);
Call<UploadFileResponse> call = apiService.uploadFile(body);
call.enqueue(new Callback<UploadFileResponse>() {
#Override
public void onResponse(Call<UploadFileResponse> call, Response<UploadFileResponse> response) {
if (response != null && response.body() != null) {
onFinished(response.body().getResult());
} else {
if (response.message() != null) {
onFinishedFailure(response.message());
}
}
}
#Override
public void onFailure(Call<UploadFileResponse> call, Throwable t) {
if (t.getLocalizedMessage() != null) {
onFinishedFailure(t.getLocalizedMessage());
}
}
});
}
In wcf service
public string uploadFile(Stream imageData)
{
string fileName = WebOperationContext.Current.IncomingRequest.Headers.Get("fileName");
string fileFullPath = "D:\\Share\\srinidhi\\Temp_" + fileName + ".Jpeg";
Image img = System.Drawing.Image.FromStream(imageData);
img.Save(fileFullPath, ImageFormat.Jpeg);
return "success";
}
In api call
#POST("RestService/json/PostUploadFile/")
Call<UploadFileResponse> uploadFile(#Body RequestBody bytes);
It seems that your stream is submitted by the form-data, which means that the stream contains some unnecessary data, such as the other data in the submitted form-data. One thing must be noted that WCF doesn’t support the form-data by default, we generally convert the data to the complete file data by using the third-party library, MultipartParser.
Here is the download page.
http://antscode.blogspot.com/2009/11/parsing-multipart-form-data-in-wcf.html
Under this circumstance, please use the below code segments to save the image.
public async Task UploadStream(Stream stream)
{
//the third-party library.
MultipartParser parser = new MultipartParser(stream);
if (parser.Success)
{
//absolute filename, extension included.
var filename = parser.Filename;
var filetype = parser.ContentType;
var ext = Path.GetExtension(filename);
using (var file = File.Create(Path.Combine(HostingEnvironment.MapPath("~/Uploads"), Guid.NewGuid().ToString() +ext)))
{
await file.WriteAsync(parser.FileContents, 0, parser.FileContents.Length);
}
}
}
If the stream is the complete binary file, please consider the below code (we use the HTTP header to save the file extension since WCF doesn’t allow to contain another parameter in the method signature).
public async Task UploadStream(Stream stream)
{
var context = WebOperationContext.Current;
string filename = context.IncomingRequest.Headers["filename"].ToString();
string ext = Path.GetExtension(filename);
using (stream)
{
//save the image under the Uploads folder on the server-side(root directory).
using (var file = File.Create(Path.Combine(HostingEnvironment.MapPath("~/Uploads"), Guid.NewGuid().ToString() + ext)))
{
await stream.CopyToAsync(file);
}
}
}
Feel free to let me know if the problem still exists.
when I try to upload any kidn of file through my SlackApp(via c# using HttpClient),
I allways get the following response:
{"ok":false,"error":"no_file_data"}
I checked my ByteArray (I stream the file to an array and then try to upload) and wrote my data back into a .txt and .jpg - I tried both types of data. When i write them back they are exact copies from the original, so I guess my streaming and writing to an ByteArrayworks fine. But something is off with my upload.
I'll show you my code:
The Client and the method to upload:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Net.Http.Headers;
namespace SlackApp
{
public class SlackClient
{
private readonly Uri _webhookUrl;
private readonly HttpClient _httpClient = new HttpClient {};
public SlackClient(Uri webhookUrl)
{
_webhookUrl = webhookUrl;
}
public async Task<HttpResponseMessage> UploadFile(byte[] file)
{
var requestContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(file);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
requestContent.Add(fileContent, "slack", "slack.txt");
var response = await _httpClient.PostAsync(_webhookUrl, requestContent);
return response;
}
}
}
the creation of the bytearray:
public class PostFile
{
String path = #"C:\Users\f.held\Desktop\Held-Docs\dagged.jpg";
public byte[] ReadImageFile()
{
FileInfo fileInfo = new FileInfo(path);
long imageFileLength = fileInfo.Length;
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
BinaryReader br = new BinaryReader(fs);
byte[] imageData = br.ReadBytes((int)imageFileLength);
return imageData;
}
}
the Main:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace SlackApp
{
class TestArea
{
public static void Main(string[] args)
{
Task.WaitAll(IntegrateWithSlackAsync());
}
private static async Task IntegrateWithSlackAsync()
{
var webhookUrl = new Uri("https://slack.com/api/files.upload?token=xoxp-hereStandsMyToken&channel=MyChannel");
var slackClient = new SlackClient(webhookUrl);
PostMessage PM = new PostMessage();
PostFile PF = new PostFile();
var testFile = PF.ReadImageFile();
while (true)
{
var message = Console.ReadLine();
FormUrlEncodedContent payload = PM.Content(message, "");
var response = await slackClient.SendMessageAsync(payload);
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content); //I build these two lines in here so I got the response from the method, and this is where it says "no_file_data"
var isValid = response.IsSuccessStatusCode ? "valid" : "invalid";
Console.WriteLine($"Received {isValid} response.");
Console.WriteLine(response); //this puts out a "valid" response - oddly enough
}
}
}
}
Does anybody have an idea what is wrong here? Why isn't it taking the data?
You have two bugs in your code:
main(): The parameter to specify the channels is called
channels, not channel
UploadFile(): When you add your file content to the multipart you
need to include the correct API parameter for the file which is file,
not slack. And also want to include a reasonable filename (instead of slack.txt).
Additional comments
UploadFile(): Its wrong to set the content type to multipart/form-data. The
correct type for that content would be image/jpeg. However, the
correct type seams to be detected automatically, so just remove the
line.
main(): The Slack API will always return OK (http 200, unless there is a network problem), so you want to also look on the ok and error properties of the JSON response instead.
Here is an update version of your code. I changed your main() method to include a call to `UploadFile()?.
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace SlackApp
{
public class PostFile
{
string path = #"C:\Users\Stratios_down.jpg";
public byte[] ReadImageFile()
{
FileInfo fileInfo = new FileInfo(path);
long imageFileLength = fileInfo.Length;
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
BinaryReader br = new BinaryReader(fs);
byte[] imageData = br.ReadBytes((int)imageFileLength);
return imageData;
}
}
public class SlackClient
{
private readonly Uri _webhookUrl;
private readonly HttpClient _httpClient = new HttpClient { };
public SlackClient(Uri webhookUrl)
{
_webhookUrl = webhookUrl;
}
public async Task<HttpResponseMessage> UploadFile(byte[] file)
{
var requestContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(file);
requestContent.Add(fileContent, "file", "stratios.jpg");
var response = await _httpClient.PostAsync(_webhookUrl, requestContent);
return response;
}
}
class TestArea
{
public static void Main(string[] args)
{
Task.WaitAll(IntegrateWithSlackAsync());
}
private static async Task IntegrateWithSlackAsync()
{
var webhookUrl = new Uri(
"https://slack.com/api/files.upload?token=xoxp-MY-TOKEN&channels=test"
);
var slackClient = new SlackClient(webhookUrl);
PostFile PF = new PostFile();
var testFile = PF.ReadImageFile();
var response = await slackClient.UploadFile(testFile);
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
Console.ReadKey();
}
}
}
In addition I would have a couple of suggestions to improve your code.
Instead of including the additional API parameters in the URL, I
would send them in the POST request as recommended by the API
documentation.
Including the file as FileStream instead of loading it yourself into
a ByteArray is the better approach and recommended for larger files.
Not sure why you need an infinite loop in your main. Those are really
bad and should be avoided.
Please also take also a look at my new async example for uploading a file to Slack where I applied those two ideas.
I was running into the no_file_data error as well. I found out you the file needs to exist AND it needs actual content inside. Make sure to do a size check or content length check in addition to the file exists check before uploading
I am trying to create a .Net Standard "Client" class for uploading (sometimes very large) files to a Controller. I want to do this by breaking the file into chunks and uploading them one at a time. The intent is for other applications to use this instead of communicating directly to the Web Api.
I already have the Controller working. I've verified that it works using a Kendo-ui control which supports chunk-saving.
The issue I am having is that the IEnumerable<IFormFile> files parameter for my controller is always empty when posted from my client class
Controller
[Route("api/Upload")]
public ActionResult ChunkSave(IEnumerable<IFormFile> files, string metaData, Guid id)
{
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(metaData));
var serializer = new DataContractJsonSerializer(typeof(ChunkMetaData));
ChunkMetaData somemetaData = serializer.ReadObject(ms) as ChunkMetaData;
// The Name of the Upload component is "files"
if (files != null)
{
// If this is the first chunk, try to delete the file so that we don't accidently
// and up appending new bytes to the old file.
if (somemetaData.ChunkIndex == 0)
{
_io.DeleteFile(id, Path.GetFileName(somemetaData.FileName));
}
foreach (var file in files)
{
// Some browsers send file names with full path. This needs to be stripped.
_io.AppendToFile(id, Path.GetFileName(somemetaData.FileName), file.OpenReadStream());
}
}
FileResult fileBlob = new FileResult();
fileBlob.uploaded = somemetaData.TotalChunks - 1 <= somemetaData.ChunkIndex;
fileBlob.fileUid = somemetaData.UploadUid;
return new JsonResult(fileBlob);
}
Client:
public class FileTransferClient
{
HttpClient Client { get; set; }
public FileTransferClient(Uri apiUrl)
{
this.Client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true })
{
BaseAddress = apiUrl
};
this.Client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<bool> UploadFile(Guid id, Stream file, string name, string contentType)
{
bool ret = true;
int chunckSize = 2097152; //2MB
int totalChunks = (int)(file.Length / chunckSize);
if (file.Length % chunckSize != 0)
{
totalChunks++;
}
for (int i = 0; i < totalChunks; i++)
{
long position = (i * (long)chunckSize);
int toRead = (int)Math.Min(file.Length - position + 1, chunckSize);
byte[] buffer = new byte[toRead];
await file.ReadAsync(buffer, 0, toRead);
MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new StringContent(id.ToString()), "id");
var meta = JsonConvert.SerializeObject(new ChunkMetaData
{
UploadUid = id.ToString(),
FileName = name,
ChunkIndex = i,
TotalChunks = totalChunks,
TotalFileSize = file.Length,
ContentType = contentType
});
content.Add(new StringContent(meta), "metaData");
using (var ms = new MemoryStream(buffer))
{
content.Add(new StreamContent(ms),"files");
var response = await Client.PostAsync("/api/Upload", content).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
ret = false;
break;
}
}
}
return ret;
}
}
Your param is empty, because you're not sending an array of files, but rather just one file. Therefore, the binding fails, and you get a null. The act of chunking (which you aren't actually even doing) does not equate to an IEnumerable<IFormFile>; it's still just an IFormFile.
While you need to send as multipart/form-data because you're sending both a file upload and some other post data, I think you're misunderstanding what this actually does. It simply means the request body contains multiple different mime-types, it does not mean that it's uploading the file in multiple parts, which seems to be what you're thinking it does.
The actual act of streaming the upload occurs on the server-side. It's about how the server chooses to handle the file being uploaded, and not so much about how the user is uploading it. More specifically, any sort of modelbinding, particular to an IFormFile will cause the file to be spooled to disk first, and then passed into your action. In other words, if you're accepting an IFormFile, you've already lost the battle. It's already been fully transferred from the client to your server.
The ASP.NET Core docs show you how to actually stream the upload, and unsurprisingly there's a fair bit of code involved, none of which you have currently. You basically have to turn modelbinding off entirely on the action and manually parse the request body yourself, being careful to actually chunk the reads from the stream and not do something that will force the entirely thing into memory at once.
The issue was that I was using a StreamContent instead of a ByteArrayContent to represent my file chunks. Here's what I ended up with:
public async Task<Bool> UploadFileAsync(Guid id, string name, Stream file)
{
int chunckSize = 2097152; //2MB
int totalChunks = (int)(file.Length / chunckSize);
if (file.Length % chunckSize != 0)
{
totalChunks++;
}
for (int i = 0; i < totalChunks; i++)
{
long position = (i * (long)chunckSize);
int toRead = (int)Math.Min(file.Length - position, chunckSize);
byte[] buffer = new byte[toRead];
await file.ReadAsync(buffer, 0, buffer.Length);
using (MultipartFormDataContent form = new MultipartFormDataContent())
{
form.Add(new ByteArrayContent(buffer), "files", name);
form.Add(new StringContent(id.ToString()), "id");
var meta = JsonConvert.SerializeObject(new ChunkMetaData
{
UploadUid = id.ToString(),
FileName = name,
ChunkIndex = i,
TotalChunks = totalChunks,
TotalFileSize = file.Length,
ContentType = "application/unknown"
});
form.Add(new StringContent(meta), "metaData");
var response = await Client.PostAsync("/api/Upload", form).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
}
return true;
}
I have a requirement to pass a file through an MVC action method.
To download it from a Web API method and return it as a result.
The code I have is assembled from a few answers here on SO and some other references.
The problem is that the file seems to be locked by the download process when I try to return it as a result. I thought that the tsk.Wait() wold solve the problem.
Perhaps someone knows of a better solution?
using (var client = HttpClientProvider.GetHttpClient())
{
client.BaseAddress = new Uri(baseAddress);
await client.GetAsync("api/Documents/" + fileName).ContinueWith(
(requestTask) =>
{
HttpResponseMessage response = requestTask.Result;
response.EnsureSuccessStatusCode();
fileName = response.Content.Headers.ContentDisposition.FileName;
if (fileName.StartsWith("\"") && fileName.EndsWith("\""))
{
fileName = fileName.Trim('"');
}
if (fileName.Contains(#"/") || fileName.Contains(#"\"))
{
fileName = Path.GetFileName(fileName);
}
path = Path.Combine(GetDocsMapPath(), fileName);
System.Threading.Tasks.Task tsk = response.Content.ReadAsFileAsync(path, true).ContinueWith(
(readTask) =>
{
Process process = new Process();
process.StartInfo.FileName = path;
process.Start();
});
tsk.Wait();
HttpResponseMessage resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StreamContent(new FileStream(path, FileMode.Open, FileAccess.Read));
resp.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
resp.Content.Headers.ContentDisposition.FileName = fileName;
return resp;
});
}
public static Task ReadAsFileAsync(this HttpContent content, string filename, bool overwrite)
{
string pathname = Path.GetFullPath(filename);
if (!overwrite && File.Exists(filename))
{
throw new InvalidOperationException(string.Format("File {0} already exists.", pathname));
}
FileStream fileStream = null;
try
{
fileStream = new FileStream(pathname, FileMode.Create, FileAccess.Write, FileShare.None);
return content.CopyToAsync(fileStream).ContinueWith(
(copyTask) =>
{
fileStream.Close();
fileStream.Dispose();
});
}
catch
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
throw;
}
}
first, I don't see any useful function achieved by the process you launch, apart from locking your file, which you don't want.
try removing these lines and retrying.
.ContinueWith(
(readTask) =>
{
Process process = new Process();
process.StartInfo.FileName = path;
process.Start();
});
Edit: Using FilePathResult
I don't know about your exact requirements, but if your goal is to return a file that you have the path for; then the easiest is to return a FilePathResult which will take care of reading and returning the contents of the file to the requester.
public FilePathResult GetFile()
{
//put your logic to determine the file path here
string name = ComputeFilePath();
//verify that the file actually exists and retur dummy content otherwise
FileInfo info = new FileInfo(name);
if (!info.Exists)
{
using (StreamWriter writer = info.CreateText())
{
writer.WriteLine("File Not Found");
}
}
return File(name, "application/octet-stream");
}
if you are sure of what type your content is , change the mime type accordingly, otherwise it's better to leave it as a binary data.