How to save a file from a windows store app in Unity - serialization

I'm making an app in Unity3D for release on the windows store.
It seems you cant write files using the .net streamwriter.
I'd like to save a csv file to a certain location and then later send it to a server using the WWW class.
I found a project which reads a file from the assets folder.
Heres the code for that...
using UnityEngine;
using System;
using System.Collections;
using System.IO;
#if NETFX_CORE
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
#endif
namespace IOS
{
public class File
{
public static object result;
#if NETFX_CORE
public static async Task<byte[]> _ReadAllBytes(string path)
{
StorageFile file = await StorageFile.GetFileFromPathAsync(path.Replace("/", "\\"));
byte[] fileBytes = null;
using (IRandomAccessStreamWithContentType stream = await file.OpenReadAsync())
{
fileBytes = new byte[stream.Size];
using (DataReader reader = new DataReader(stream))
{
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(fileBytes);
}
}
return fileBytes;
}
#endif
public static IEnumerator ReadAllText(string path)
{
#if NETFX_CORE
Task<byte[]> task = _ReadAllBytes(path);
while (!task.IsCompleted)
{
yield return null;
}
UTF8Encoding enc = new UTF8Encoding();
result = enc.GetString(task.Result, 0, task.Result.Length);
#else
yield return null;
result = System.IO.File.ReadAllText(path);
#endif
}
}
}
public class Example : MonoBehaviour
{
private string data;
IEnumerator ReadFile(string path)
{
yield return StartCoroutine(IOS.File.ReadAllText(path));
data = IOS.File.result as string;
}
public void OnGUI()
{
string path = Path.Combine(Application.dataPath, "StreamingAssets/Data.txt");
if (GUILayout.Button("Read file '" + path + "'"))
{
StartCoroutine(ReadFile(path));
}
GUILayout.Label(data == null ? "<NoData>" : data);
}
}
Heres the MSDN docs for serializing with Windows Store apps
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh758325.aspx
I'm wondering how to adapt this to suit my purposes. ie. Write a file to a specific location that I can reference later when I am sending the file via WWW.

The main issue is the location. The Application.dataPath is read only data within the app's package. To write data use Application.persistentDataPath to get a writable location in the application data folder.
Unity provides alternatives to System.IO.File with its UnityEngine.Windows.File object. You can just switch the using between System.IO and UnityEngine.Windows then call File.ReadAllBytes or File.WriteAllBytes regardless of platform.
This is essentially what your code snippit is doing, except that Unity already provides it.

Related

ASP.NET Core MVC - Image is not getting uploaded in live website directory

I have my ASP.NET Core MVC website on production server of godaddy windows hosting. I have a "Create article" page which allows to select an image from PC and upload it in the project folder directory.
This functionality works fine on local server, but it doesn't work on production server. I have tried to change the upload path of the image multiple times, but none of them works for me. I get an error on production. If I don't upload the image and create article without it, it works fine on the production.
Paths which I have tried :
public static string ArticleImgPath = #"\images\Articles\";
public static string ArticleImgPath = "/images/Articles/";
public static string ArticleImgPath = "https://derawala.org/wwwroot/images/Articles/";
public static string ArticleImgPath = "https://derawala.org/httpdocs/wwwroot/images/Articles/";
public static string ArticleImgPath = "https://derawala.org/images/Articles/";
This is the error I got on production :
If I remove the image uploading code, I don't get any errors and the app works just fine.
I have also made sure to upload appsettings.production.json file to make sure that environment variables are set for production environment. Also, all the other functionalities of website including database operations work properly except those which have file uploading in it.
Here is my controller method for article creation:
public IActionResult ArticlePost(ParentForApply ParentVM)
{
var files = HttpContext.Request.Form.Files;
if (files.Count == 0)
{
ParentVM.ArticleModel.Img ="defltimg.png";
_db.Articles.Add(ParentVM.ArticleModel);
_db.SaveChanges();
return View(ParentVM);
}
else
{
string upload = WC.ArticleImgPath;
string fileName = Guid.NewGuid().ToString();
string extension = Path.GetExtension(files[0].FileName);
string fullpath = upload + fileName + extension;
using (var filestream = new FileStream(fullpath, FileMode.Create))
{
files[0].CopyTo(filestream);
}
ParentVM.ArticleModel.Img = fileName + extension;
_db.Articles.Add(ParentVM.ArticleModel);
_db.SaveChanges();
return View(ParentVM);
}
}
The error indicates you swap to development environment to get detailed errors,not means you are not in production enviroment
Alos,you could read this document related with uploading files:https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-6.0
you could try with the codes in this document to upload file and store the content into database:
public class AppFile
{
public int Id { get; set; }
public byte[] Content { get; set; }
}
in controller:
using (var memoryStream = new MemoryStream())
{
await FileUpload.FormFile.CopyToAsync(memoryStream);
// Upload the file if less than 2 MB
if (memoryStream.Length < 2097152)
{
var file = new AppFile()
{
Content = memoryStream.ToArray()
};
_dbContext.File.Add(file);
await _dbContext.SaveChangesAsync();
}
else
{
ModelState.AddModelError("File", "The file is too large.");
}
Update:
Could you swap to development environment to see the detailed error message?
Have you tried to write the path with WebRootPath?
public IActionResult New([FromServices]IHostingEnvironment env)
{
......
using (var stream = new FileStream(Path.Combine(env.WebRootPath, "...."), FileMode.CreateNew))
.....
}

how to read excel file in memory (without saving it in disk) and return its content dotnet core

Im working on a webApi using dotnet core that takes the excel file from IFormFile and reads its content.Iam following the article
https://levelup.gitconnected.com/reading-an-excel-file-using-an-asp-net-core-mvc-application-2693545577db which is doing the same thing except that the file here is present on the server and mine will be provided by user.
here is the code:
public IActionResult Test(IFormFile file)
{
List<UserModel> users = new List<UserModel>();
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var stream = System.IO.File.Open(file.FileName, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
while (reader.Read()) //Each row of the file
{
users.Add(new UserModel
{
Name = reader.GetValue(0).ToString(),
Email = reader.GetValue(1).ToString(),
Phone = reader.GetValue(2).ToString()
});
}
}
}
return Ok(users);
}
}
When system.IO tries to open the file, it could not find the path as the path is not present. How it is possible to either get the file path (that would vary based on user selection of file)? are there any other ways to make it possible.
PS: I dont want to upload the file on the server first, then read it.
You're using the file.FileName property, which refers to the file name the browser send. It's good to know, but not a real file on the server yet. You have to use the CopyTo(Stream) Method to access the data:
public IActionResult Test(IFormFile file)
{
List<UserModel> users = new List<UserModel>();
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var stream = new MemoryStream())
{
file.CopyTo(stream);
stream.Position = 0;
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
while (reader.Read()) //Each row of the file
{
users.Add(new UserModel{Name = reader.GetValue(0).ToString(), Email = reader.GetValue(1).ToString(), Phone = reader.GetValue(2).ToString()});
}
}
}
return Ok(users);
}
Reference

Upload file, check for same file name

Hi i'am using this utils, to make file upload and delete upload. MVC 4 LINQ to SQL.
I would like to check if file is already uploaded, and if, make a meassage
to try new file.
Can you help me, getting started, to add code for this ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
namespace CFire2.SupplyConUtils
{
public static class FileUpload
{
public static char DirSeparator =
System.IO.Path.DirectorySeparatorChar;
public static string FilesPath = "Content" +
DirSeparator + "SupplyUpload" + DirSeparator;
public static string UploadFile(HttpPostedFileBase file)
{
// Check if we have a file
if (null == file) return "";
// Make sure the file has content
if (!(file.ContentLength > 0)) return "";
string fileName = file.FileName;
string fileExt = Path.GetExtension(file.FileName);
// Make sure we were able to determine a proper
// extension
if (null == fileExt) return "";
// Check if the directory we are saving to exists
if (!Directory.Exists(FilesPath))
{
// If it doesn't exist, create the directory
Directory.CreateDirectory(FilesPath);
}
//// Set our full path for saving
var path = Path.Combine(HttpContext.Current.Server.MapPath("~/Content/SupplyUpload"), fileName);
// Save our file
file.SaveAs(path);
// Return the filename
return fileName;
}
public static void DeleteFile(string fileName)
{
// Don't do anything if there is no name
if (fileName.Length == 0) return;
// Set our full path for deleting
var path = Path.Combine(HttpContext.Current.Server.MapPath("~/Content/SupplyUpload"), fileName);
// Check if our file exists
if (File.Exists(path))
{
// Delete our file
File.Delete(path);
}
}
}
}
The MSDN docs for HttpPostedFileBase.FileName says
When overridden in a derived class, gets the fully qualified name of
the file on the client.
So probably you need to add this line to correctly execute your check
string fileName = Path.GetFileName(file.FileName);
and then
var path = Path.Combine(HttpContext.Current.Server.MapPath("~/Content/SupplyUpload"),
fileName);
if(File.Exists(path))
return "The file has been already uploaded!
....

ZipEntry to Stream with monodroid

I want to unzip a file in Android using Monodroid. I can get my ZipEntry with the Property NextEntry, but now I really need to convert this ZipEntry to a simple Stream.
EDIT:
Some part of my code
using System;
using System.IO;
using Java.Util.Zip;
using File = System.IO.File;
public void ExtractFile(Stream ZipFile, Action<String, Stream> WriteFile)
{
ZipInputStream zis;
try
{
zis = new ZipInputStream(ZipFile);
ZipEntry entry;
byte[] buffer = new byte[1024];
int count;
while ((entry = zis.NextEntry) != null)
{
// HERE I need to call my WriteFile action with a stream
}
...
Thanks
If you are using Ionic.Zip, then you can get it easily:
zipentry.InputStream

ASP.NET WebApi file upload using guid and file extension

I currently am able to save a file being uploaded to a WebAPI controller, but I'd like to be able to save the file as a guid with the correct file name extension so it can be viewed correctly.
Code:
[ValidationFilter]
public HttpResponseMessage UploadFile([FromUri]string AdditionalInformation)
{
var task = this.Request.Content.ReadAsStreamAsync();
task.Wait();
using (var requestStream = task.Result)
{
try
{
// how can I get the file extension of the content and append this to the file path below?
using (var fileStream = File.Create(HttpContext.Current.Server.MapPath("~/" + Guid.NewGuid().ToString())))
{
requestStream.CopyTo(fileStream);
}
}
catch (IOException)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.Created;
return response;
}
I can't seem to get a handle on the actual filename of the content. I thought headers.ContentDisposition.FileName might be a candidate but that doesn't seem to get populated.
Thanks for the comments above which pointed me in the right direction.
To clarify the final solution, I used a MultipartFormDataStreamProvider which streams the file automatically. The code is in another question I posted to a different problem here:
MultipartFormDataStreamProvider and preserving current HttpContext
My full provider code is listed below. The key to generating the guid file name is to override the GetLocalFileName function and use the headers.ContentDisposition property. The provider handles the streaming of the content to file.
public class MyFormDataStreamProvider : MultipartFormDataStreamProvider
{
public MyFormDataStreamProvider (string path)
: base(path)
{ }
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
// restrict what images can be selected
var extensions = new[] { "png", "gif", "jpg" };
var filename = headers.ContentDisposition.FileName.Replace("\"", string.Empty);
if (filename.IndexOf('.') < 0)
return Stream.Null;
var extension = filename.Split('.').Last();
return extensions.Any(i => i.Equals(extension, StringComparison.InvariantCultureIgnoreCase))
? base.GetStream(parent, headers)
: Stream.Null;
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
// override the filename which is stored by the provider (by default is bodypart_x)
string oldfileName = headers.ContentDisposition.FileName.Replace("\"", string.Empty);
string newFileName = Guid.NewGuid().ToString() + Path.GetExtension(oldfileName);
return newFileName;
}
}