So far, after some research from the internet I have been able to select .jpeg files from the computer and upload it to the firebase using Unity C#.
But, I cannot figure out, how I should modify the code below to use it for uploading .txt files as well.
If there is some other simpler way to do this task please tell that (if any). Otherwise, tell how I should modify this code so that it fulfills the purpose mentioned above.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//For Picking files
using System.IO;
using SimpleFileBrowser;
//For firebase storage
using Firebase;
using Firebase.Extensions;
using Firebase.Storage;
public class UploadFile : MonoBehaviour
{
FirebaseStorage storage;
StorageReference storageReference;
// Start is called before the first frame update
void Start()
{
FileBrowser.SetFilters(true, new FileBrowser.Filter("Images", ".jpg", ".png"), new FileBrowser.Filter("Text Files", ".txt", ".pdf"));
FileBrowser.SetDefaultFilter(".jpg");
FileBrowser.SetExcludedExtensions(".lnk", ".tmp", ".zip", ".rar", ".exe");
storage = FirebaseStorage.DefaultInstance;
storageReference = storage.GetReferenceFromUrl("gs://app_name.appspot.com/");
}
public void OnButtonClick()
{
StartCoroutine(ShowLoadDialogCoroutine());
}
IEnumerator ShowLoadDialogCoroutine()
{
yield return FileBrowser.WaitForLoadDialog(FileBrowser.PickMode.FilesAndFolders, true, null, null, "Load Files and Folders", "Load");
Debug.Log(FileBrowser.Success);
if (FileBrowser.Success)
{
// Print paths of the selected files (FileBrowser.Result) (null, if FileBrowser.Success is false)
for (int i = 0; i < FileBrowser.Result.Length; i++)
Debug.Log(FileBrowser.Result[i]);
Debug.Log("File Selected");
byte[] bytes = FileBrowserHelpers.ReadBytesFromFile(FileBrowser.Result[0]);
//Editing Metadata
var newMetadata = new MetadataChange();
newMetadata.ContentType = "image/jpeg";
//Create a reference to where the file needs to be uploaded
StorageReference uploadRef = storageReference.Child("uploads/newFile.jpeg");
Debug.Log("File upload started");
uploadRef.PutBytesAsync(bytes, newMetadata).ContinueWithOnMainThread((task) => {
if (task.IsFaulted || task.IsCanceled)
{
Debug.Log(task.Exception.ToString());
}
else
{
Debug.Log("File Uploaded Successfully!");
}
});
}
}
}
As far as I understand it is basically already working as expected but you need to handle the different file types / extensions differently.
I think all you need to actually do is
use the correct file name + extension
use the correct ContentType depending on the file extension
So maybe something like
IEnumerator ShowLoadDialogCoroutine()
{
yield return FileBrowser.WaitForLoadDialog(FileBrowser.PickMode.FilesAndFolders, true, null, null, "Load Files and Folders", "Load");
Debug.Log(FileBrowser.Success);
if (!FileBrowser.Success)
{
yield break;
}
//foreach (var file in FileBrowser.Result)
//{
// Debug.Log(file);
//}
var file = FileBrowser.Result[0];
Debug.Log("File Selected: \"{file}\"");
// e.g. C:\someFolder/someFile.txt => someFile.txt
var fileNameWithExtension = file.Split('/', '\\').Last();
if (!fileNameWithExtension.Contains('.'))
{
throw new ArgumentException($"Selected file \"{file}\" is not a supported file!");
}
// e.g. someFile.txt => txt
var extensionWithoutDot = fileNameWithExtension.Split('.').Last();
// Get MIME type according to file extension
var contentType = extensionWithoutDot switch
{
"jpg" => $"image/jpeg",
"jpeg" => $"image/jpeg",
"png" => $"image/png",
"txt" => "text/plain",
"pdf" => "application/pdf",
_ => throw new ArgumentException($"Selected file \"{file}\" of type \"{extensionWithoutDot}\" is not supported!")
};
// Use dynamic content / MIME type
var newMetadata = new MetadataChange()
{
ContentType = contentType
};
// Use the actual selected file name including extension
StorageReference uploadRef = storageReference.Child($"uploads/{fileNameWithExtension}");
Debug.Log("File upload started");
uploadRef.PutBytesAsync(bytes, newMetadata).ContinueWithOnMainThread((task) =>
{
if (task.IsFaulted || task.IsCanceled)
{
Debug.LogException(task.Exception);
}
else
{
Debug.Log($"File \"{file}\" Uploaded Successfully!");
}
});
}
you most probably will want to replace the throw by proper error handling with user feedback later ;)
Related
I have angular project as my client side and .Net 6 web api project as my backend. I am still new to both technologies. I am creating a website and there is a functionality that I am trying to add and haven't been successful so far. I want to upload images into a .Net web api project images folder using angular. I also want to later access those images from angular project. I want to store the path of the image files in the database. I have tried to check for the code on the internet without success. Your assistance will be appreciated.
First, submit your's files from frontend with FormData
postWithFile(url: string, obj: any, files: File[]) {
let cloneHeader: any = {};
let options: any = {
headers: new HttpHeaders(cloneHeader),
observe: 'response',
responseType: 'json'
};
let formData: FormData = new FormData();
if (typeof obj == 'object') { // obj is external submit data
formData.append('data', JSON.stringify(obj));
} else {
formData.append('data', obj);
}
if (files && files.length > 0) {
files.forEach((ds, index) => {
formData.append('file_' + index, ds, ds.name);
});
}
return this._http
.post(this.host + url, formData, options)
.pipe(map((res: any) => {
return res.body;
}));
}
And backend handle request with HttpContext.Current.Request.Files, save images to server and store path of images in database
[HttpPost]
public ResponseMessage<bool?> UploadImages()
{
var response = new ResponseMessage<bool?>();
try
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
ExternalDataModel model = MessageConvert.DeserializeObject<ExternalDataModel>(HttpContext.Current.Request["data"]); // obj in frontend
//
List<string> listImages = new List<string>();
DateTime now = DateTime.Now;
string buildPath = $"{string.Format("{0:00}", now.Year)}\\{string.Format("{0:00}", now.Month)}\\{string.Format("{0:00}", now.Day)}"; // change by your's folder path
foreach (string file in HttpContext.Current.Request.Files)
{
var fileContent = HttpContext.Current.Request.Files[file];
int fileLength = fileContent.ContentLength;
if (fileContent != null && fileLength > 0)
{
var stream = fileContent.InputStream;
byte[] imgByteArray;
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
imgByteArray = memoryStream.ToArray();
}
string fileName = $"format_file_name_if_need_{fileContent.FileName}";
string RelativeFolder = $"{buildPath}";
string AbsoluteFolder = Path.Combine("FOLDER_IN_SERVER_FULL_PATH", RelativeFolder);
if (!Directory.Exists(AbsoluteFolder))
{
Directory.CreateDirectory(AbsoluteFolder);
}
string pathSave = Path.Combine(RelativeFolder, fileName);
FileHelper.SaveFileFromBinaryArray(pathSave, imgByteArray);
listImages.Add(pathSave);
}
}
// model.listImages = listImages; // assign to model to save to DB
//
// var data = _bus.uploadImage(model);
// if (data)
// {
// response.Data = true;
// response.MessageCode = MessageCodes.UpdateSuccessfully;
// }
}
catch (Exception ex)
{
response.MessageCode = ex.Message;
}
return response;
}
I created a simple Azure Function that creates thumbnails for images uploaded to a container on Azure which gets triggered by a BlobTrigger.
It's working fine but because my container has both image files as well as other types e.g. PDF, Excel, Word, etc., the function gets triggered by all these files.
I thought I could address this by making sure that we only process image files using the code below. It kind of works because it only processes image files but it still seems to create placeholder blobs for other file types in the target container.
For example, if it detects a file named myfile.pdf in the source container, it still creates a myfile.pdf in the target container but it's 0 Bytes.
How do I make sure that a non-image files completely get skipped and not even create placeholders in my target container?
[FunctionName("ImageResizer")]
public async Task Run([BlobTrigger("my-source-container/{name}", Connection = "myconnection")] Stream input, string name, [Blob("my-thumbnails-container/{name}", FileAccess.Write, Connection = "myconnection")] Stream outputBlob, ILogger log)
{
try
{
var fileExtension = FileUtils.GetFileExtension(name);
if (!string.IsNullOrEmpty(fileExtension))
{
if (fileExtension.ToLower() == "png" || fileExtension.ToLower() == "jpg" || fileExtension.ToLower() == "jpeg")
{
using (var image = Image.Load(input))
{
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(150, 150),
Mode = ResizeMode.Crop
}));
using (var ms = new MemoryStream())
{
if(fileExtension.ToLower() == "png")
await image.SaveAsPngAsync(outputBlob);
else if(fileExtension.ToLower() == "jpg" || fileExtension.ToLower() == "jpeg")
await image.SaveAsJpegAsync(outputBlob);
}
}
log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {input.Length} Bytes");
}
}
}
catch (Exception ex)
{
log.LogInformation(ex.Message, null);
}
}
When using declarative bindings using attributes this is unavoidable unless you can somehow filter out unwanted blobs using the BlobTrigger attribute. It's default behavior for an output binding to expect the binding to be necessary so it is created as soon as the Function is executed.
However, with .Net languages you can use runtime bindings so only blobs that are actually handled will lead to output files, see the docs. That way you have more control regarding when an output blob is created.
[FunctionName("ImageResizer")]
public async Task Run([BlobTrigger("my-source-container/{name}", Connection = "myconnection")] Stream input, string name, IBinder binder, ILogger log)
{
try
{
var fileExtension = FileUtils.GetFileExtension(name);
if (!string.IsNullOrEmpty(fileExtension))
{
if (fileExtension.ToLower() == "png" || fileExtension.ToLower() == "jpg" || fileExtension.ToLower() == "jpeg")
{
using (var image = Image.Load(input))
{
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(150, 150),
Mode = ResizeMode.Crop
}));
var attribute = new BlobAttribute("my-thumbnails-container/{name}", FileAccess.Write);
attribute.Connection = "myconnectionstring";
using (var ms = new MemoryStream())
using (var stream = await binder.BindAsync<Stream>(attribute))
{
if (fileExtension.ToLower() == "png")
await image.SaveAsPngAsync(stream);
else if (fileExtension.ToLower() == "jpg" || fileExtension.ToLower() == "jpeg")
await image.SaveAsJpegAsync(stream);
}
}
log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {input.Length} Bytes");
}
}
}
catch (Exception ex)
{
log.LogInformation(ex.Message, null);
}
}
Here is also a blogpost outlining what I just did.
I'm working on an ASP.NET Core 5 project. I have this action method:
public async Task<IActionResult> CreateV3EnterCheckFile(IFormFile MarksFile)
{
var filesCount = Directory.GetFiles("Uploads").Length;
string path = Path.Combine("Uploads", filesCount + 1 + ".xlsx");
await MarksFile.SaveToAsync(path);
var xlImporter = new XLImporter();
var importedData = await xlImporter.ImportSheetAsync(path, 0);
var r = (from x in importedData select new { ID = x[0], StudentId = x[1] }).ToList();
System.IO.File.Delete(path);
return View();
}
I tried to get IFormFile uploaded file by the user to save it on the server and querying it using one of my projects (that uses LinqToExcel library).
I am querying the data and everything is perfect I still have just one problem it is this line of code:
System.IO.File.Delete(path);
It throws an exception and the message is I can't delete that file because it is still being used by another process.
I'm very sure that the process is related to the LinqToExcel library.
More details :
SaveToAsync is an extension method created by me that is its definition
public static Task SaveToAsync(this IFormFile file, string pathToSaveTo)
{
return Task.Factory.StartNew(() =>
{
using (Stream fileStream = File.Open(pathToSaveTo, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
file.CopyTo(fileStream);
}
});
}
Please - is there any way or method or solution to delete this file even if it is being used by another process?
Massive thanks in advance.
Based on the source code of ExcelQueryFactory (https://github.com/paulyoder/LinqToExcel/blob/master/src/LinqToExcel/ExcelQueryFactory.cs) I would try the following:
ExcelQueryFactory has a ReadOnly Property. For read only access (if applicable) I would set it to true when creating the instance.
More important: IExcelQueryFactory implements IDisposable, so you can (should) use a using block:
using (var excelFile = new ExcelQueryFactory(pathToExcelFile) {ReadOnly = true})
{
// Do your work.
}
Of course you can use using var ..., but if you need a more reduced scope, the "old" using syntax allows more control.
I assumed that your Uploads folder is under webroot.
You can try this:-
public YourControllerName(IHostingEnvironment he) //input parameter
{
_he = he;
}
public async Task<IActionResult> CreateV3EnterCheckFile(IFormFile MarksFile)
{
try
{
var filesCount = Directory.GetFiles("Uploads").Length;
string contentRootPath = _he.ContentRootPath;
string path = Path.Combine(contentRootPath +"\\Uploads", filesCount + 1 + ".xlsx");
await MarksFile.SaveToAsync(path);
var xlImporter = new XLImporter();
var importedData = await xlImporter.ImportSheetAsync(path, 0);
var r = (from x in importedData select new { ID = x[0], StudentId = x[1] }).ToList();
//System.IO.File.Delete(path);
if (File.Exists(path))
{
File.Delete(path);
}
else
{
Debug.WriteLine("File does not exist.");
}
return View();
}
catch(Exception e)
{
Console.WriteLine(e);
}
Or you can try another process:-
try
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.IO.File.Delete(path);
}
catch(Exception e){
}
}
Or this:-
if (System.IO.File.Exists(path))
{
try
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.IO.File.Delete(path);
}
catch (Exception e) { }
}
it should resolve your issue I hope. by the way, if your Upload folder is not under the webroot path. you can find your path using your process.
I connect to Firebase from Unity, and get simple image from it. But I want to know how can I save this image for example in Assets folder of unity project?
void Start()
{
FirebaseStorage storage = FirebaseStorage.DefaultInstance;
Firebase.Storage.StorageReference path_reference = storage.GetReference("background/1.jpg")
"SavetoAssets = storage.GetReference("background/1.jpg")" // something like that.
}
This post is almost what you need. I just expanded it with the FileIO part using FileStream.WriteAsync and used UnityWebRequestTexture instead of WWW:
private void Start()
{
FirebaseStorage storage = FirebaseStorage.DefaultInstance;
Firebase.Storage.StorageReference path_reference = storage.GetReference("background/1.jpg");
path_reference.GetValueAsync().ContinueWith(
task =>
{
Debug.Log("Default Instance entered");
if (task.IsFaulted)
{
Debug.Log("Error retrieving data from server");
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
string data_URL = snapshot.GetValue(true).ToString();
//Start coroutine to download image
StartCoroutine(DownloadImage(data_URL));
}
}
);
}
private IEnumerator DownloadImage(string url)
{
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url))
{
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
// Instead of accessing the texture
//var texture = DownloadHandlerTexture.GetContent(uwr);
// you can directly get the bytes
var bytes = DownloadHandlerTexture.GetData(uwr);
// and write them to File e.g. using
using (FileStream fileStream = File.Open(filename, FileMode.OpenOrCreate))
{
fileStream.WriteAsync(bytes, 0, bytes.Length);
}
}
}
}
Note: I don't use Firebase, Android nor iOs so I couldn't test this
I have some data to save into a database.
I have created a web api post method to save data. Following is my post method:
[Route("PostRequirementTypeProcessing")]
public IEnumerable<NPAAddRequirementTypeProcessing> PostRequirementTypeProcessing(mdlAddAddRequirementTypeProcessing requTypeProcess)
{
mdlAddAddRequirementTypeProcessing rTyeProcessing = new mdlAddAddRequirementTypeProcessing();
rTyeProcessing.szDescription = requTypeProcess.szDescription;
rTyeProcessing.iRequirementTypeId = requTypeProcess.iRequirementTypeId;
rTyeProcessing.szRequirementNumber = requTypeProcess.szRequirementNumber;
rTyeProcessing.szRequirementIssuer = requTypeProcess.szRequirementIssuer;
rTyeProcessing.szOrganization = requTypeProcess.szOrganization;
rTyeProcessing.dIssuedate = requTypeProcess.dIssuedate;
rTyeProcessing.dExpirydate = requTypeProcess.dExpirydate;
rTyeProcessing.szSignedBy = requTypeProcess.szSignedBy;
rTyeProcessing.szAttachedDocumentNo = requTypeProcess.szAttachedDocumentNo;
if (String.IsNullOrEmpty(rTyeProcessing.szAttachedDocumentNo))
{
}
else
{
UploadFile();
}
rTyeProcessing.szSubject = requTypeProcess.szSubject;
rTyeProcessing.iApplicationDetailsId = requTypeProcess.iApplicationDetailsId;
rTyeProcessing.iEmpId = requTypeProcess.iEmpId;
NPAEntities context = new NPAEntities();
Log.Debug("PostRequirementTypeProcessing Request traced");
var newRTP = context.NPAAddRequirementTypeProcessing(requTypeProcess.szDescription, requTypeProcess.iRequirementTypeId,
requTypeProcess.szRequirementNumber, requTypeProcess.szRequirementIssuer, requTypeProcess.szOrganization,
requTypeProcess.dIssuedate, requTypeProcess.dExpirydate, requTypeProcess.szSignedBy,
requTypeProcess.szAttachedDocumentNo, requTypeProcess.szSubject, requTypeProcess.iApplicationDetailsId,
requTypeProcess.iEmpId);
return newRTP.ToList();
}
There is a field called 'szAttachedDocumentNo' which is a document that's being saved in the database as well.
After saving all data, I want the physical file of the 'szAttachedDocumentNo' to be saved on the server. So i created a method called "UploadFile" as follows:
[HttpPost]
public void UploadFile()
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
// Get the uploaded file from the Files collection
var httpPostedFile = HttpContext.Current.Request.Files["UploadedFile"];
if (httpPostedFile != null)
{
// Validate the uploaded image(optional)
string folderPath = HttpContext.Current.Server.MapPath("~/UploadedFiles");
//string folderPath1 = Convert.ToString(ConfigurationManager.AppSettings["DocPath"]);
//Directory not exists then create new directory
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// Get the complete file path
var fileSavePath = Path.Combine(folderPath, httpPostedFile.FileName);
// Save the uploaded file to "UploadedFiles" folder
httpPostedFile.SaveAs(fileSavePath);
}
}
}
Before running the project, i debbugged the post method, so when it comes to "UploadFile" line, it takes me to its method.
From the file line, it skipped the remaining lines and went to the last line; what means it didn't see any file.
I am able to save everything to the database, just that i didn't see the physical file in the specified location.
Any help would be much appreciated.
Regards,
Somad
Makes sure the request "content-type": "multipart/form-data" is set
[HttpPost()]
public async Task<IHttpActionResult> UploadFile()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
MultipartMemoryStreamProvider provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
if (provider.Contents != null && provider.Contents.Count == 0)
{
return BadRequest("No files provided.");
}
foreach (HttpContent file in provider.Contents)
{
string filename = file.Headers.ContentDisposition.FileName.Trim('\"');
byte[] buffer = await file.ReadAsByteArrayAsync();
using (MemoryStream stream = new MemoryStream(buffer))
{
// save the file whereever you want
}
}
return Ok("files Uploded");
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}