I am a beginner in unit testing. I am trying to write a test script, but I am stuck. I'd like to understand what I am doing wrong.
public ConvertExcelDocumentResponse ConvertExcelDocument(ConvertExcelDocumentRequest request)
{
var logger = Bootstrapper.Resolve<ILogger>();
ConvertExcelDocumentResponse response = new ConvertExcelDocumentResponse();
if (!string.IsNullOrEmpty(request.FileName) &&
System.IO.Path.GetExtension(request.FileName).ToLowerInvariant() == ".xls" &&
request.FileContent != null && request.FileContent.Length > 0)
{
response.FileContent = _httpClient.ConvertExcelDocument(request.FileContent, request.FileName);
response.FileName = System.IO.Path.GetFileNameWithoutExtension(request.FileName) + ".xlsx";
response.Success = true;
}
else
{
logger.LogError("Unable to convert Excel Document '" + request.FileName + "' only .xls files are allowed.");
response.FileContent = new byte[] { };
response.Success = false;
}
return response;
}
Here is my test:
/// <summary>
/// Test to Convert XLS to XLSX
/// </summary>
[TestCategory("DocumentService")]
[TestMethod]
public void Test_ConvertExcelDocumentResponse()
{
byte[] filecontent = new byte[] { };
string filename = "file";
using (ShimsContext.Create())
{
ConvertExcelDocumentRequest excelDocumentRequest1 = new ConvertExcelDocumentRequest()
{
FileName = "filename",
FileContent = new byte[] {},
};
ConvertExcelDocumentRequest excelDocumentRequest2 = new ConvertExcelDocumentRequest()
{
FileName = "",
FileContent = new byte[] {},
};
ShimDocumentService.AllInstances.ConvertExcelDocumentConvertExcelDocumentRequest = ((#this, Success) =>
{
return new ConvertExcelDocumentResponse()
{
FileContent=filecontent,
Success = true,
FileName = filename,
};
});
ShimHttpClientBase.AllInstances.ConvertWordDocumentByteArrayString = ((#this, fileContent, fileName) =>
{
return fileContent;
});
//Act
ILogger logger = Bootstrapper.Resolve<ILogger>();
var docService = new DocumentService();
//IDocumentService DocumentService = Bootstrapper.Resolve<IDocumentService>();
var excelResponse1 = docService.ConvertExcelDocument(excelDocumentRequest1);
var excelResponse2 = docService.ConvertExcelDocument(excelDocumentRequest2);
//Assert
Assert.AreEqual(excelResponse1.FileContent, filecontent );
Assert.AreEqual(excelResponse1.FileName, filename);
Assert.IsTrue(excelResponse1.Success, "Expects true");
Assert.AreEqual(excelResponse2.FileContent, filecontent);
Assert.AreEqual(excelResponse2.FileName, filename);
Assert.IsTrue(excelResponse2.Success, "Expects true");
}
}
This is passing the test but code coverage is 0%. I don't know where I am going wrong. I tried covering filename = null and few other conditions. not sure why it isn't covering my code
It looks like you'll need to also write a test where the filename is null or empty. That way you will cover the code in the else block.
Increasing code coverage often involves making your test exercise pieces of code that are not likely to be normally hit.
Related
I am trying to put a list of file paths into a zip file and download them in my controller but nothing is happening. I haven't gotten any errors and nothing pops up as downloaded after executing the program.
Controller:
[HttpPost]
public ActionResult ExportFile(ClientStatement_Inventory model, string[] years, string[] months, string[] radio, string[] emails, string acctNum)
{
List<string> allPaths = new List<string>();
// Is Account Number empty or Radio not selected
if (String.IsNullOrEmpty(acctNum.Trim()) && radio == null)
{
return Json(new { success = false, message = "* Please Enter Account Number\n\n * Please choose whether to download or email statements" });
}
if (acctNum == "")
{
return Json(new { success = false, message = "* Please Enter Account Number" });
}
else if (radio == null)
{
return Json(new { success = false, message = "* Please Choose whether To Download or Email" });
}
//try
//{
// If months and years are selected
if (months != null)
{
if (years != null)
{
using (var db = new DB())
{
// Get Specific years and months
allPaths = db.ClientStatement_Inventory
.Where(x => x.accountNum == acctNum && years.Contains(x.statementYear) && months.Contains(x.statementMonth))
.Select(c => c.statementPath).ToList();
}
if (allPaths.Count == 0)
{
return Json(new { success = false, message = $"* There were no documents for Account#: {acctNum}" });
}
}
//return View();
}
//Downloading Statements is chosen
if (radio[0] == "Download Statements")
{
foreach (var path in allPaths)
{
byte[] fileBytes = System.IO.File.ReadAllBytes(path);
var result = fileBytes;
string filename = Path.GetFileName("sjdfk.pdf");
string realpath = Server.MapPath("~/Downloads");
string fullPath = Path.Combine(realpath, filename);
string contentType = "application/zip";
string file = builder.ToString();
return File(fullPath, contentType, "Helo");
}
}
//}
//catch (Exception ex)
//{
// //error
// return Redirect("Home/Index");
//}
return Redirect("Home/Index");
}
}
}
If you see my comment //Downloading statement is chosen. Thats where I am trying to download the files. I know my foreach loop is very wrong but nothing is happening I can't even get and error or one file to download.
Also my allPaths variable is an array of multiple strings of the server path name of the files. so they would look like \ipt-siru1\filename
I just don't know what else to try. I've tried so many different things today and still can't get this figured out.
I haven't tested this but you could try something like this to save it into the memorystream:
var downloadFileName = string.Format("YourDownload-{0}.zip", DateTime.Now.ToString("yyyy-MM-dd-HH_mm_ss"));
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + downloadFileName);
using (Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile())
{
zipFile.AddDirectoryByName("Files");
foreach (var path in allPaths)
{
zipFile.AddFile(path);
}
//zipFile.Save(Response.OutputStream);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = downloadFileName,
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
var memStream = new MemoryStream();
zipFile.Save(memStream);
memStream.Position = 0; // Else it will try to read starting at the end
return File(memStream, "application/octet-stream");
//Response.Close();
}
Currently building a web api for the existing web-based media services to encode uploaded videos.
The goal of my solution is to create a api call where i'll be sending the mp4 link and do the processing (encoding and streaming of the given mp4 link). I was able to fetch the mp4 and download to the server and reupload to its own blob storage. However if I check the AMS explorer, every parameters I passed exists except for the filesize. Here's my WEB API call I created (a total replicate of the existing media service form method. (https://tiltestingstreaming.azurewebsites.net/
)
[HttpPost]
public JsonResult UploadApi(String video_url)
{
var id = 1;
WebClient client = new WebClient();
var videoStream = new MemoryStream(client.DownloadData(video_url));
var container = CloudStorageAccount.Parse(mediaServiceStorageConnectionString).CreateCloudBlobClient().GetContainerReference(mediaServiceStorageContainerReference);
container.CreateIfNotExists();
var fileName = Path.GetFileName(video_url);
var fileToUpload = new CloudFile()
{
BlockCount = 1,
FileName = fileName,
Size = videoStream.Length,
BlockBlob = container.GetBlockBlobReference(fileName),
StartTime = DateTime.Now,
IsUploadCompleted = false,
UploadStatusMessage = string.Empty
};
Session.Add("CurrentFile", fileToUpload);
byte[] chunk = new byte[videoStream.Length];
//request.InputStream.Read(chunk, 0, Convert.ToInt32(request.Length));
//JsonResult returnData = null;
string fileSession = "CurrentFile";
CloudFile model = (CloudFile)Session[fileSession];
var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(
string.Format(CultureInfo.InvariantCulture, "{0:D4}", id)));
try
{
model.BlockBlob.PutBlock(
blockId,
videoStream, null, null,
new BlobRequestOptions()
{
RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(10), 3)
},
null);
}
catch (StorageException e)
{
model.IsUploadCompleted = true;
model.UploadStatusMessage = "Failed to Upload file. Exception - " + e.Message;
return Json(new { error = true, isLastBlock = false, message = model.UploadStatusMessage });
}
var blockList = Enumerable.Range(1, (int)model.BlockCount).ToList<int>().ConvertAll(rangeElement => Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0:D4}", rangeElement))));
model.BlockBlob.PutBlockList(blockList);
var duration = DateTime.Now - model.StartTime;
float fileSizeInKb = model.Size / 1024;
string fileSizeMessage = fileSizeInKb > 1024 ? string.Concat((fileSizeInKb / 1024).ToString(CultureInfo.CurrentCulture), " MB") : string.Concat(fileSizeInKb.ToString(CultureInfo.CurrentCulture), " KB");
model.UploadStatusMessage = string.Format(CultureInfo.CurrentCulture, "File of size {0} took {1} seconds to upload.", fileSizeMessage, duration.TotalSeconds);
IAsset mediaServiceAsset = CreateMediaAsset(model);
model.AssetId = mediaServiceAsset.Id;
//if (id == model.BlockCount){CommitAllChunks(model);}
return Json(new { error = false, isLastBlock = false, message = string.Empty, filename = fileName,filesize = videoStream.Length });
}
Functions used on the form-method solution.
[HttpPost]
public ActionResult SetMetadata(int blocksCount, string fileName, long fileSize)
{
var container = CloudStorageAccount.Parse(mediaServiceStorageConnectionString).CreateCloudBlobClient().GetContainerReference(mediaServiceStorageContainerReference);
container.CreateIfNotExists();
var fileToUpload = new CloudFile()
{
BlockCount = blocksCount,
FileName = fileName,
Size = fileSize,
BlockBlob = container.GetBlockBlobReference(fileName),
StartTime = DateTime.Now,
IsUploadCompleted = false,
UploadStatusMessage = string.Empty
};
Session.Add("CurrentFile", fileToUpload);
return Json(true);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult UploadChunk(int id)
{
HttpPostedFileBase request = Request.Files["Slice"];
byte[] chunk = new byte[request.ContentLength];
request.InputStream.Read(chunk, 0, Convert.ToInt32(request.ContentLength));
JsonResult returnData = null;
string fileSession = "CurrentFile";
if (Session[fileSession] != null)
{
CloudFile model = (CloudFile)Session[fileSession];
returnData = UploadCurrentChunk(model, chunk, id);
if (returnData != null)
{
return returnData;
}
if (id == model.BlockCount)
{
return CommitAllChunks(model);
}
}
else
{
returnData = Json(new
{
error = true,
isLastBlock = false,
message = string.Format(CultureInfo.CurrentCulture, "Failed to Upload file.", "Session Timed out")
});
return returnData;
}
return Json(new { error = false, isLastBlock = false, message = string.Empty });
}
private JsonResult UploadCurrentChunk(CloudFile model, byte[] chunk, int id)
{
using (var chunkStream = new MemoryStream(chunk))
{
var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(
string.Format(CultureInfo.InvariantCulture, "{0:D4}", id)));
try
{
model.BlockBlob.PutBlock(
blockId,
chunkStream, null, null,
new BlobRequestOptions()
{
RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(10), 3)
},
null);
return null;
}
catch (StorageException e)
{
model.IsUploadCompleted = true;
model.UploadStatusMessage = "Failed to Upload file. Exception - " + e.Message;
return Json(new { error = true, isLastBlock = false, message = model.UploadStatusMessage });
}
}
}
private ActionResult CommitAllChunks(CloudFile model)
{
model.IsUploadCompleted = true;
bool errorInOperation = false;
try
{
var blockList = Enumerable.Range(1, (int)model.BlockCount).ToList<int>().ConvertAll(rangeElement => Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0:D4}", rangeElement))));
model.BlockBlob.PutBlockList(blockList);
var duration = DateTime.Now - model.StartTime;
float fileSizeInKb = model.Size / 1024;
string fileSizeMessage = fileSizeInKb > 1024 ? string.Concat((fileSizeInKb / 1024).ToString(CultureInfo.CurrentCulture), " MB") : string.Concat(fileSizeInKb.ToString(CultureInfo.CurrentCulture), " KB");
model.UploadStatusMessage = string.Format(CultureInfo.CurrentCulture, "File of size {0} took {1} seconds to upload.", fileSizeMessage, duration.TotalSeconds);
IAsset mediaServiceAsset = CreateMediaAsset(model);
model.AssetId = mediaServiceAsset.Id;
}
catch (StorageException e)
{
model.UploadStatusMessage = "Failed to upload file. Exception - " + e.Message;
errorInOperation = true;
}
return Json(new
{
error = errorInOperation,
isLastBlock = model.IsUploadCompleted,
message = model.UploadStatusMessage,
assetId = model.AssetId
});
}
private IAsset CreateMediaAsset(CloudFile model)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(mediaServiceStorageConnectionString);
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer mediaBlobContainer = cloudBlobClient.GetContainerReference(mediaServiceStorageContainerReference);
mediaBlobContainer.CreateIfNotExists();
// Create a new asset.
IAsset asset = context.Assets.Create("UploadedVideo-" + Guid.NewGuid().ToString().ToLower(), AssetCreationOptions.None);
IAccessPolicy writePolicy = context.AccessPolicies.Create("writePolicy", TimeSpan.FromMinutes(120), AccessPermissions.Write);
ILocator destinationLocator = context.Locators.CreateLocator(LocatorType.Sas, asset, writePolicy);
// Get the asset container URI and copy blobs from mediaContainer to assetContainer.
Uri uploadUri = new Uri(destinationLocator.Path);
string assetContainerName = uploadUri.Segments[1];
CloudBlobContainer assetContainer = cloudBlobClient.GetContainerReference(assetContainerName);
string fileName = HttpUtility.UrlDecode(Path.GetFileName(model.BlockBlob.Uri.AbsoluteUri));
var sourceCloudBlob = mediaBlobContainer.GetBlockBlobReference(fileName);
sourceCloudBlob.FetchAttributes();
if (sourceCloudBlob.Properties.Length > 0)
{
IAssetFile assetFile = asset.AssetFiles.Create(fileName);
var destinationBlob = assetContainer.GetBlockBlobReference(fileName);
destinationBlob.DeleteIfExists();
destinationBlob.StartCopy(sourceCloudBlob);
destinationBlob.FetchAttributes();
if (sourceCloudBlob.Properties.Length != destinationBlob.Properties.Length)
model.UploadStatusMessage += "Failed to copy as Media Asset!";
}
destinationLocator.Delete();
writePolicy.Delete();
sourceCloudBlob.Delete(); //delete temp blob
// Refresh the asset.
asset = context.Assets.Where(a => a.Id == asset.Id).FirstOrDefault();
var ismAssetFiles = asset.AssetFiles.FirstOrDefault();
ismAssetFiles.IsPrimary = true;
ismAssetFiles.Update();
model.UploadStatusMessage += " Media file uploaded successfully by id: " + asset.Id;
model.AssetId = asset.Id;
return asset;
}
[HttpPost]
public ActionResult EncodeToAdaptiveBitrateMP4s(string assetId)
{
// Note: You need atleast 1 reserve streaming unit for dynamic packaging of encoded media. If you don't have that, you can't see video file playing.
IAsset inputAsset = GetAssetById(assetId);
string token = string.Empty;
string uploadFileOriginalName = string.Empty;
////// Without preset (say default preset), works very well
//IJob job = context.Jobs.CreateWithSingleTask(MediaProcessorNames.AzureMediaEncoder,
// MediaEncoderTaskPresetStrings.H264AdaptiveBitrateMP4Set720p,
// inputAsset,
// "UploadedVideo-" + Guid.NewGuid().ToString().ToLower() + "-Adaptive-Bitrate-MP4",
// AssetCreationOptions.None);
//job.Submit();
//IAsset encodedOutputAsset = job.OutputMediaAssets[0];
//// XML Preset
IJob job = context.Jobs.Create(inputAsset.Name);
IMediaProcessor processor = GetLatestMediaProcessorByName("Media Encoder Standard");
string configuration = System.IO.File.ReadAllText(HttpContext.Server.MapPath("~/MediaServicesCustomPreset.xml"));
ITask task = job.Tasks.AddNew(inputAsset.Name + "- encoding task", processor, configuration, TaskOptions.None);
task.InputAssets.Add(inputAsset);
task.OutputAssets.AddNew(inputAsset.Name + "-Adaptive-Bitrate-MP4", AssetCreationOptions.None);
job.Submit();
IAsset encodedAsset = job.OutputMediaAssets[0];
// process policy & encryption
ProcessPolicyAndEncryption(encodedAsset);
// Get file name
string fileSession = "CurrentFile";
if (Session[fileSession] != null)
{
CloudFile model = (CloudFile)Session[fileSession];
uploadFileOriginalName = model.FileName;
}
// Generate Streaming URL
string smoothStreamingUri = GetStreamingOriginLocator(encodedAsset, uploadFileOriginalName);
// add jobid and output asset id in database
AzureMediaServicesContext db = new AzureMediaServicesContext();
var video = new Video();
video.RowAssetId = assetId;
video.EncodingJobId = job.Id;
video.EncodedAssetId = encodedAsset.Id;
video.LocatorUri = smoothStreamingUri;
video.IsEncrypted = useAESRestriction;
db.Videos.Add(video);
db.SaveChanges();
if (useAESRestriction)
{
token = AzureMediaAsset.GetTestToken(encodedAsset.Id, encodedAsset);
}
// Remove session
Session.Remove("CurrentFile");
// return success response
return Json(new
{
error = false,
message = "Congratulations! Video is uploaded and pipelined for encoding, check console log for after encoding playback details.",
assetId = assetId,
jobId = job.Id,
locator = smoothStreamingUri,
encrypted = useAESRestriction,
token = token
});
}
The actual challenge that I encounter was, I'm not sure why the filesize of the downloaded remote mp4 file doesn't store in the media services asset file yet I was able to return the value via the json response of the my api call. Please check attached Screenshot of the API response.
Was able to figure out my own problem. All I need to do is to copy the function of my encoding function that was bind to an ActionResult data type. I think ActionResult is part of the form-method solution and I am building a WebAPI call solution of the working form-method.
From the original call function
[HttpPost] public ActionResult EncodeToAdaptiveBitrateMP4s(string assetId)
I copy the entire function into my WebApi Call function, like this:
[HttpPost]
public JsonResult UploadApi(String video_url)
{
var id = 1;
WebClient client = new WebClient();
var videoStream = new MemoryStream(client.DownloadData(video_url));
var container = CloudStorageAccount.Parse(mediaServiceStorageConnectionString).CreateCloudBlobClient().GetContainerReference(mediaServiceStorageContainerReference);
container.CreateIfNotExists();
var fileName = Path.GetFileName(video_url);
var fileToUpload = new CloudFile()
{
BlockCount = 1,
FileName = fileName,
Size = videoStream.Length,
BlockBlob = container.GetBlockBlobReference(fileName),
StartTime = DateTime.Now,
IsUploadCompleted = false,
UploadStatusMessage = string.Empty
};
Session.Add("CurrentFile", fileToUpload);
byte[] chunk = new byte[videoStream.Length];
//request.InputStream.Read(chunk, 0, Convert.ToInt32(request.Length));
//JsonResult returnData = null;
string fileSession = "CurrentFile";
CloudFile model = (CloudFile)Session[fileSession];
var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(
string.Format(CultureInfo.InvariantCulture, "{0:D4}", id)));
try
{
model.BlockBlob.PutBlock(
blockId,
videoStream, null, null,
new BlobRequestOptions()
{
RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(10), 3)
},
null);
}
catch (StorageException e)
{
model.IsUploadCompleted = true;
model.UploadStatusMessage = "Failed to Upload file. Exception - " + e.Message;
return Json(new { error = true, isLastBlock = false, message = model.UploadStatusMessage });
}
var blockList = Enumerable.Range(1, (int)model.BlockCount).ToList<int>().ConvertAll(rangeElement => Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0:D4}", rangeElement))));
model.BlockBlob.PutBlockList(blockList);
var duration = DateTime.Now - model.StartTime;
float fileSizeInKb = model.Size / 1024;
string fileSizeMessage = fileSizeInKb > 1024 ? string.Concat((fileSizeInKb / 1024).ToString(CultureInfo.CurrentCulture), " MB") : string.Concat(fileSizeInKb.ToString(CultureInfo.CurrentCulture), " KB");
model.UploadStatusMessage = string.Format(CultureInfo.CurrentCulture, "File of size {0} took {1} seconds to upload.", fileSizeMessage, duration.TotalSeconds);
IAsset mediaServiceAsset = CreateMediaAsset(model);
model.AssetId = mediaServiceAsset.Id;
// Note: You need atleast 1 reserve streaming unit for dynamic packaging of encoded media. If you don't have that, you can't see video file playing.
var assetId = model.AssetId;
IAsset inputAsset = GetAssetById(assetId);
string token = string.Empty;
string uploadFileOriginalName = string.Empty;
////// Without preset (say default preset), works very well
//IJob job = context.Jobs.CreateWithSingleTask(MediaProcessorNames.AzureMediaEncoder,
// MediaEncoderTaskPresetStrings.H264AdaptiveBitrateMP4Set720p,
// inputAsset,
// "UploadedVideo-" + Guid.NewGuid().ToString().ToLower() + "-Adaptive-Bitrate-MP4",
// AssetCreationOptions.None);
//job.Submit();
//IAsset encodedOutputAsset = job.OutputMediaAssets[0];
//// XML Preset
IJob job = context.Jobs.Create(inputAsset.Name);
IMediaProcessor processor = GetLatestMediaProcessorByName("Media Encoder Standard");
string configuration = System.IO.File.ReadAllText(HttpContext.Server.MapPath("~/MediaServicesCustomPreset.xml"));
ITask task = job.Tasks.AddNew(inputAsset.Name + "- encoding task", processor, configuration, TaskOptions.None);
task.InputAssets.Add(inputAsset);
task.OutputAssets.AddNew(inputAsset.Name + "-Adaptive-Bitrate-MP4", AssetCreationOptions.None);
job.Submit();
IAsset encodedAsset = job.OutputMediaAssets[0];
// process policy & encryption
ProcessPolicyAndEncryption(encodedAsset);
// Get file name
uploadFileOriginalName = model.FileName;
// Generate Streaming URL
string smoothStreamingUri = GetStreamingOriginLocator(encodedAsset, uploadFileOriginalName);
// add jobid and output asset id in database
AzureMediaServicesContext db = new AzureMediaServicesContext();
var video = new Video();
video.RowAssetId = assetId;
video.EncodingJobId = job.Id;
video.EncodedAssetId = encodedAsset.Id;
video.LocatorUri = smoothStreamingUri;
video.IsEncrypted = useAESRestriction;
db.Videos.Add(video);
db.SaveChanges();
if (useAESRestriction)
{
token = AzureMediaAsset.GetTestToken(encodedAsset.Id, encodedAsset);
}
// Remove session
Session.Remove("CurrentFile");
// return success response
return Json(new
{
error = false,
message = "Congratulations! Video is uploaded and pipelined for encoding, check console log for after encoding playback details.",
assetId = assetId,
jobId = job.Id,
locator = smoothStreamingUri,
encrypted = useAESRestriction,
token = token
});
//if (id == model.BlockCount){CommitAllChunks(model);}
//return Json(new { error = false, isLastBlock = false, message = string.Empty, filename = fileName,filesize = videoStream.Length });
}
However this kind of solution is to rigid and not for a long term solution but the concept was there and able to meet my goal. I will just redo my code and re-create a more flexible solution.
NOTE: I am not a C# developer. Respect for the beginner like me.
I have a controller action which is returning a file using the following line of code. However, I need return a filestream with a model which may contain some other JSON serialized data.
I've tried a few different approaches but haven't found a solution which seems to work very well. Has anyone done this before and what is considered the best approach?
this.File(fs, "application/zip", fileName);
Note: The question answered here is referring to multiple FileStreams, whereas I need to return back a FileStream and a Model as JSON.
However, I have modified this solution with the following. What does everyone think?
return new MultipartResult()
{
new MultipartFileContent()
{
ContentType = "application/zip",
FileName = fileName,
Stream = this.OpenFile(filePath)
},
new MultipartModelContent()
{
ContentType = "application/json",
Model = model
}
};
public async Task ExecuteResultAsync(ActionContext context)
{
foreach (var item in this.OfType<MultipartFileContent>())
{
if (item.Stream != null)
{
var c = new StreamContent(item.Stream);
if (item.ContentType != null)
{
c.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(item.ContentType);
}
if (item.FileName != null)
{
var contentDisposition = new ContentDispositionHeaderValue("attachment");
contentDisposition.SetHttpFileName(item.FileName);
c.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
c.Headers.ContentDisposition.FileName = contentDisposition.FileName.Value;
c.Headers.ContentDisposition.FileNameStar = contentDisposition.FileNameStar.Value;
}
this.content.Add(c);
}
}
foreach (var item in this.OfType<MultipartModelContent>())
{
var c = new ObjectContent<object>(item, new JsonMediaTypeFormatter(), item.ContentType);
if (item.ContentType != null)
{
c.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(item.ContentType);
}
this.content.Add(c);
}
context.HttpContext.Response.ContentLength = content.Headers.ContentLength;
context.HttpContext.Response.ContentType = content.Headers.ContentType.ToString();
await content.CopyToAsync(context.HttpContext.Response.Body);
}
Now I have successfully working code (with multiple threads) for items bulk import in IN202500 screen in Acumatica.
The problem is that I am struggling to import an image of an item and actually I don't have an image by itself but only URL link to this image.
So, my question is has anyone done this in c#?
This is my piece of code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ItemImportMultiThreaded
{
public class ItemImporter
{
private IN202500.Screen _itemsScreen;
private static object _itemsSchemaLock = new object();
private static IN202500.Content _itemsSchema;
public void Login(string url, string username, string password, string company)
{
Console.WriteLine("[{0}] Logging in to {1}...", System.Threading.Thread.CurrentThread.ManagedThreadId, url);
_itemsScreen = new IN202500.Screen();
_itemsScreen.Url = url + "/PMSDB/(W(2))/Soap/IN202500.asmx";
_itemsScreen.EnableDecompression = true;
_itemsScreen.CookieContainer = new System.Net.CookieContainer();
_itemsScreen.Timeout = 36000;
_itemsScreen.Login(username, password);
Console.WriteLine("[{0}] Logged in to {1}.", System.Threading.Thread.CurrentThread.ManagedThreadId, url);
lock (_itemsSchemaLock)
{
// Threads can share the same schema.
if (_itemsSchema == null)
{
Console.WriteLine("[{0}] Retrieving IN202500 schema...", System.Threading.Thread.CurrentThread.ManagedThreadId);
_itemsSchema = _itemsScreen.GetSchema();
if (_itemsSchema == null) throw new Exception("IN202500 GetSchema returned null. See AC-73433.");
}
}
}
public void Logout()
{
_itemsScreen.Logout();
}
public void Import(List<Item> items)
{
Console.WriteLine("[{0}] Submitting {1} items to Acumatica...", System.Threading.Thread.CurrentThread.ManagedThreadId, items.Count);
var commands = new IN202500.Command[]
{
_itemsSchema.StockItemSummary.InventoryID,
_itemsSchema.StockItemSummary.Description,
_itemsSchema.GeneralSettingsItemDefaults.ItemClass,
_itemsSchema.VendorDetails.VendorID,
_itemsSchema.VendorDetails.VendorInventoryID,
_itemsSchema.VendorDetails.ServiceCommands.NewRow,
_itemsSchema.VendorDetails.VendorID,
_itemsSchema.VendorDetails.VendorInventoryID,
_itemsSchema.VendorDetails.ServiceCommands.NewRow,
_itemsSchema.VendorDetails.VendorID,
_itemsSchema.VendorDetails.VendorInventoryID,
_itemsSchema.CrossReference.AlternateID,
_itemsSchema.CrossReference.Description,
_itemsSchema.Actions.Save
};
string[][] data = new string[items.Count][];
int count = 0;
foreach(Item item in items)
{
data[count] = new string[11];
data[count][0] = item.InventoryID;
data[count][1] = item.Description.Trim();
data[count][2] = item.ItemClassID;
data[count][3] = item.DigiKey;
data[count][4] = item.DKPN;
data[count][5] = item.Mouser;
data[count][6] = item.MouserID;
data[count][7] = item.Element14;
data[count][8] = item.Element14ID;
data[count][9] = item.AlternateID;
data[count][10] = item.Descr;
count++;
}
_itemsScreen.Import(commands, null, data, false, true, true);
Console.WriteLine("[{0}] Submitted {1} items to Acumatica.", System.Threading.Thread.CurrentThread.ManagedThreadId, items.Count);
}
}
}
I tried to use FileStream but that didn't work.
If by URL link you mean an external http resource, you can download the image and upload it.
The StockItems image field cycle through all the images contained in the Files popup in the order they are displayed:
I uploaded the images from a static external Url using the following code:
const string imageUrl = "https://cdn.acumatica.com/media/2016/03/software-technology-industries-small.jpg";
string path = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetTempFileName(), ".jpg"));
// Download Image
using (WebClient client = new WebClient())
{
client.DownloadFile(new Uri(imageUrl), path);
}
// ReadUploadFile function below
byte[] data = ReadUploadFile(path);
_itemsScreen.Import(new IN202500.Command[]
{
// Get Inventory Item
new Value
{
Value = "D1",
LinkedCommand = _itemsSchema.StockItemSummary.InventoryID,
},
_itemsSchema.Actions.Save,
// Upload Inventory Item Image
new Value
{
FieldName = Path.GetFileName(path),
LinkedCommand = _itemsSchema.StockItemSummary.ServiceCommands.Attachment
},
_itemsSchema.Actions.Save
},
null,
new string[][]
{
new string[]
{
// Image data
Convert.ToBase64String(data)
}
},
false,
false,
true);
public byte[] ReadUploadFile(string filePath)
{
byte[] filedata;
using (FileStream file = File.Open(filePath,
FileMode.Open,
FileAccess.ReadWrite,
FileShare.ReadWrite))
{
filedata = new byte[file.Length];
file.Read(filedata, 0, filedata.Length);
}
if (filedata == null || filedata.Length == 0)
{
throw new Exception(string.Concat("Invalid or empty file: ", filePath));
}
return filedata;
}
You can try using the below, Tested Code.
var content = _context.CR306000GetSchema(); _context.CR306000Clear();
var commands = new List();
ReqParameter(content, ref commands);
commands.Add(content.Actions.Save);
commands.Add(content.CaseSummary.CaseID);
var orderResults = _context.CR306000Submit(commands.ToArray());
private static void ReqParameter(CR306000Content content, ref List cmds) { if (cmds == null) throw new ArgumentNullException("cmds");
private static void ReqParameter(CR306000Content content, ref List<Command> cmds)
{
if (cmds == null) throw new ArgumentNullException("cmds");
byte[] filedata= null;
Uri uri = new Uri("https://cdn.acumatica.com/media/2016/03/software-technology-industries-small.jpg"); // change the required url of the data that has to be fetched
if (uri.IsFile)
{
string filename = System.IO.Path.GetFileName(uri.LocalPath);
filedata = System.Text.Encoding.UTF8.GetBytes(uri.LocalPath);
}
if (filedata == null)
{
WebClient wc = new WebClient();
filedata = wc.DownloadData(uri);
}
cmds = new List<Command>
{
//Case Header Details
new Value { Value="<NEW>",LinkedCommand = content.CaseSummary.CaseID},
new Value { Value="L41",LinkedCommand = content.CaseSummary.ClassID},
new Value { Value="ABCSTUDIOS",LinkedCommand = content.CaseSummary.BusinessAccount, Commit = true},
new Value { Value="Test subject created from envelop call 11C",LinkedCommand = content.CaseSummary.Subject},
// body of the case
new Value{Value= "Body of the content for created through envelop call 11B", LinkedCommand = content.Details.Description},
//Attaching a file
new Value
{
Value = Convert.ToBase64String(filedata), // byte data that is passed to through envelop
FieldName = "Test.jpg",
LinkedCommand =
content.CaseSummary.ServiceCommands.Attachment
},
};
}
Let me know if this works for you.
Thanks
I am working on a Adobe Echosign demo C# winforms application. My code is a direct copy of their command line code (with modifications), however, my code returns an error after it transmits the data.
This is the command line code from EchoSign that works
public static void sendDocument(string apiKey, string fileName, string formFieldLayerTemplateKey, string recipient)
{
FileStream file = getTestPdfFile(fileName);
secure.echosign.com.FileInfo[] fileInfos = new secure.echosign.com.FileInfo[1];
fileInfos[0] = new secure.echosign.com.FileInfo(fileName, null, file);
SenderInfo senderInfo = null;
string[] recipients = new string[1];
recipients[0] = recipient;
DocumentCreationInfo documentInfo = new DocumentCreationInfo(
recipients,
testPrefix + Path.GetFileName(file.Name),
testMessage,
fileInfos,
SignatureType.ESIGN,
SignatureFlow.SENDER_SIGNATURE_NOT_REQUIRED
);
if (formFieldLayerTemplateKey != null)
{
secure.echosign.com.FileInfo[] formFieldLayerTemplates = new secure.echosign.com.FileInfo[1];
formFieldLayerTemplates[0] = new secure.echosign.com.FileInfo(formFieldLayerTemplateKey);
documentInfo.formFieldLayerTemplates = formFieldLayerTemplates;
}
DocumentKey[] documentKeys;
documentKeys = ES.sendDocument(apiKey, senderInfo, documentInfo);
Console.WriteLine("Document key is: " + documentKeys[0].documentKey);
}
This is my code block that returns an error from their system:
public static void sendDocument(string apiKey, string fileName, string formFieldLayerTemplateKey, string recipient)
{
try
{
SenderInfo senderInfo = new SenderInfo();
senderInfo = null;
FileStream FileToSign = getTestPdfFile(fileName);
byte[] bytes = System.IO.File.ReadAllBytes("C:\\PROJECTS\\TestFile.pdf");
secure.echosign.com.FileInfo[] fileInfos = new secure.echosign.com.FileInfo[1];
fileInfos[0] = new EchoSignTest.secure.echosign.com.FileInfo();
fileInfos[0].fileName = fileName;
fileInfos[0].mimeType = null;
fileInfos[0].file = bytes;
RecipientInfo[] docRecipient = new RecipientInfo[1];
docRecipient[0] = new RecipientInfo();
docRecipient[0].email = recipient;
DocumentCreationInfo documentInfo = new DocumentCreationInfo();
documentInfo.recipients = docRecipient;
documentInfo.name = testPrefix + Path.GetFileName(FileToSign.Name);
documentInfo.message = testMessage;
documentInfo.fileInfos = fileInfos;
documentInfo.signatureType = SignatureType.ESIGN;
documentInfo.signatureFlow = SignatureFlow.SENDER_SIGNATURE_NOT_REQUIRED;
if (formFieldLayerTemplateKey != null)
{
secure.echosign.com.FileInfo[] formFieldLayerTemplates = new secure.echosign.com.FileInfo[1];
formFieldLayerTemplates[0] = new secure.echosign.com.FileInfo();
formFieldLayerTemplates[0].formKey = formFieldLayerTemplateKey;
documentInfo.formFieldLayerTemplates = formFieldLayerTemplates;
}
EchoSignDocumentService19PortTypeClient ES = new EchoSignDocumentService19PortTypeClient();
DocumentKey[] documentKeys = new DocumentKey[1];
documentKeys = ES.sendDocument(apiKey, senderInfo, documentInfo);
Console.WriteLine("Document key is: " + documentKeys[0].documentKey);
}
catch (NullReferenceException ex)
{
string errMessage = ex.Message;
}
catch (Exception ex)
{
string errMessage = ex.Message;
}
}
What is different between the two code blocks? The error may reside in the FileInfo[] or DocumentCreationInfo() blocks. I am perhaps not creating the objects as the system requires.
Any suggestions are appreciated.
The error seems to be the direct assignment of the bytes of the document you read to the fileInfos[0].file variable. In the documentation for FileInfo it states that the file parameter has to be the raw file content, encoded using base64, but you assign the raw file content without encoding it. When the constructor is called with a file stream like in your first example (the command-line one), the constructor seems to handle this automatically. You could try to change these lines in your Winforms example:
FileStream FileToSign = getTestPdfFile(fileName);
byte[] bytes = System.IO.File.ReadAllBytes("C:\\PROJECTS\\TestFile.pdf");
secure.echosign.com.FileInfo[] fileInfos = new secure.echosign.com.FileInfo[1];
fileInfos[0] = new EchoSignTest.secure.echosign.com.FileInfo();
fileInfos[0].fileName = fileName;
fileInfos[0].mimeType = null;
fileInfos[0].file = bytes;
into this code and try if this works:
FileStream FileToSign = getTestPdfFile(fileName);
secure.echosign.com.FileInfo[] fileInfos = new secure.echosign.com.FileInfo[1];
fileInfos[0] = new secure.echosign.com.FileInfo(fileName, null, FileToSign);
You should use the provided constructors instead of direct assignment to make sure all variables/parameters are handled properly.
The error you told about in your comments about the constructor that doesn't take 3 arguments could be a result of the EchoSignTest. prefix before your constructor call as it seems this is your own program's namespace and not the right namespace of the provided API.