What I have :
I have a simple module that would convert a string to a text file & store it in my server
C#/Unity Code
private IEnumerator UploadUserData(string _fileName)
{
string _data = ("With text name " + System.DateTime.Now.ToString ());
string _postDataURL = "https://nameofserver.com/upload.php"
WWWForm _form = new WWWForm ();
_form.AddField ("name", _fileName);
_form.AddField ("data", _data);
UnityWebRequest _wwwRequest = UnityWebRequest.Post (_postDataURL, _form);
yield return _wwwRequest.Send ();
while (!_wwwRequest.isDone)
{ yield return null;}
if (_wwwRequest.error != null)
{
Debug.Log (_wwwRequest.error);
}
else
{
Debug.Log ("Uploaded");
}
Debug.Log (_wwwRequest.downloadHandler.text);
}
Server Side PHP
<?php
if(isset($_POST['name']) && isset($_POST['data'])){
file_put_contents($_POST['name'].".txt", $_POST['data']);
echo "uploaded.";
}else{
echo "invalid file uploaded.";
}
?>
Request
I want to build a system where I could upload files to specific folders. Let's say I to upload a text file (filename.txt) to a folder name "Folder1".
From php side
The php side should create a folder "Folder1" if it is not present
then upload the text file "filename.txt" to that folder
if "Folder1" exist in that directory, then I would like the php
script to upload that text file "filename.txt" to the existing folder
"Folder1"
From Unity Side
How should I mention the folder name from Unity webrequest?
Thank you very much for your time. Much appreciate it.
Like I was ranting at you on IRC, you should avoid allowing your file paths to be dictated by, or even include, data supplied by a user or even a user-accessible API.
I would suggest something along the lines of:
// always have something define the absolute path to your application root,
// then build your paths/filenames relative to that.
// let's say this is /usr/local/myapp/config/config.php
define('APPROOT', realpath(__DIR__ . '/..')); // APPROOT == '/usr/local/myapp'
define('USERUPLOADS', APPROOT . '/user_uploads');
// userid SHOULD be something you control, not a username or anything specified
// by the user. the focus is to prevent malformed and/or malevolent user data
// from breaking out of the upload directory sandbox.
function acceptUploadedUserData($userid, $name, $data) {
$userdir = USERUPLOADS . '/' . $userid;
if( ! is_dir($userdir) ) {
mkdir($userdir);
}
// just kinda baseline "OK"
if( strpos('..', $name) !== false || strpos('/', $name) !== false ) {
throw new Exception('Specified name cannot contain .. or /');
}
file_put_contents($userdir . '/' . $name, $data);
// better yet don't let the user have *any* control over any part of the path
// but also allows you to specify *any* string as the filename.
file_put_contents($userdir . '/' . md5($name), $data);
}
acceptUploadedUserData($_SESSION['user_id'], $_POST['name'], $_POST['data']);
Related
I have Three strings that are set to the current: year, month, day respectively using the "DateTimeNow". and I have set a path to a string of the root folder named "Custom_project" by grabbing the file path from a text file on the desktop.
They are named:
public string CurrentYear;
public string CurrentMonth;
public string CurrentDay;
public string CustomOrderFilePathTopFolder = File.ReadAllText("C:/desktop/Custom_project.txt");
//CustomOrderFilePathTopFolder now ='s C:/desktop/Custom_project/
Okay so I am trying to check to see if a folder exists(Folder Named: "CurrentYear" or in this case "2020" inside of the: "Custom_project" folder) and if not then create the folder with the string, if it does exist then it will then proceed to my next step which is essentially opening the file: "CurrentYear" or "2020, then repeating the same thing but inside of that folder: Custom_project/2020, for month and repeat one last time for day.
So In the end I would have a file path that looks like so: "C:/desktop/Custom_project/2020/07/12".
Now To My Question:
"HOW DO I GO ABOUT CHECKING IF A FILE NAMED "2020" EXISTS INSIDE OF THE CUSTOMPATHFOLDER AND IF IT DOESN'T THEN CREATE THAT FOLDER
I just tried using This(Which doesn't seem to work):
if (CustomOrderFilePathTopFolder == "")
{
MessageBox.Show("ERROR FILE PATH CANNOT BE EMPTY!");
}
else if (!Directory.Exists(CustomOrderFilePathTopFolder + CurrentYear))
{
Directory.CreateDirectory(CustomOrderFilePathTopFolder + CurrentYear);
}
This Does nothing for me so I tried this:
if (CustomOrderFilePathTopFolder == "")
{
MessageBox.Show("ERROR FILE PATH CANNOT BE EMPTY!");
}
else if (!Directory.Exists(CustomOrderFilePathTopFolder + "/" + CurrentYear))
{
Directory.CreateDirectory(CustomOrderFilePathTopFolder + "/" + CurrentYear);
}
Doesn't Work Either So I am At a loss Please Let me know how I would go about this please and thank you a ton!!
Try below steps
you need to first combine path to point proper file/folder
Check File exists or not
If not, then create folder with same name.
using System.IO;
...
var filePath = Path.Combine(CustomOrderFilePathTopFolder, CurrentYear))
if (!File.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
I'm creating a game using C# and trying to incorporate a CSV for parsing previous scores into a leaderboard and also writing to the file when a player finishes their game.
This is the data stored relating to a score
If this was a sole project I would store the csv in the bin > Debug folder and pass the file path to a StreamReader. Although, this is a group project using Azure Devops/TFS as source control so I'm not too sure what way is best to do this.
I have tried storing the CSV in the Resources of the project but I didn't realise this embeds the file in the project and only allows for reading from the file.
The CSV is currently read like:
var file = Properties.Resources.highscores;
char[] splitter = "\r\n".ToCharArray();
string[] scoresCsv = Properties.Resources.highscores.Split(splitter);
foreach (string score in scoresCsv)
{
if(!String.IsNullOrEmpty(score))
{
var values = score.Split(',');
highScores.Add(new HighScore(values[0], Convert.ToInt32(values[1]), Convert.ToDateTime(values[2])));
}
}
this.highScores = highScores.OrderByDescending(x => x.Score).ToList();
Select the "Team Explorer" window and go to "Source Control Explorer"
Here you will see a global view of the project.
You can add files to your project in any folder you wish outside of the actual source. If you want to you can add your bin folder into the source control and keep that file in the bin folder.
Where-ever you put the file you just need to know the location to it from your project and you are able to map to it and edit it in runtime.
Another option is to create a folder in the C:\ProgramData folder for your game and you can write the leaderboards directly into their C drive when they run the game. People would be able to modify the leaderboards but, obviously the game is for learning purposes of coding and usually you wouldn't store the leaderboards on the client side anyway it would be on a server.
This assumes that the high score data is not shared, and stores it locally. It doesn't require the file to be added to source control.
public class ScoreFileHandler
{
private static string appPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "YourAppName");
private static string scoreFileName = "highscores.txt";
private static string filePath = Path.Combine(appPath, scoreFileName);
public string ReadFile()
{
if (!Directory.Exists(appPath))
{
Directory.CreateDirectory(appPath);
}
if (File.Exists(filePath))
{
return File.ReadAllText(filePath);
}
return string.Empty; // TODO - caller needs to handle this
}
public void WriteFile(string csvScoreData)
{
if (!Directory.Exists(appPath))
{
Directory.CreateDirectory(appPath);
}
File.WriteAllText(filePath, csvScoreData);
}
}
I'm working on a Unity app right now, and the client wants to be able to allow the user to click a button and have a PDF open on the phone (iOS and Android), but in whatever app they use for reading PDFs and not inside of the Unity app.
I've tried Application.OpenURL("file:///[...]"), and that works in the editor on my Windows desktop (opens PDF in Edge)... but it doesn't open the file on Android (nothing happens to open the file).
Here's what I'm doing:
public string FileName = string.Empty;
public string FileExtension = string.Empty;
private string FilePath = string.Empty;
void Start()
{
FilePath = string.Format("{0}/{1}.{2}", Application.persistentDataPath, FileName, FileExtension);
}
public void OpenFile()
{
if (!File.Exists(FilePath))
{
var pathRoot = Application.streamingAssetsPath;
#if UNITY_ANDROID && !UNITY_EDITOR
pathRoot = Application.dataPath + "!/assets";
#endif
StartCoroutine(FetchFile(string.Format("{0}/{1}.{2}", pathRoot, FileName, FileExtension)));
return;
}
Application.OpenURL(FilePath);
}
private IEnumerator FetchFile(string path)
{
path = "file://" + path;
#if UNITY_ANDROID && !UNITY_EDITOR
path = "jar:" + path;
#endif
var fetcher = new WWW(path);
yield return fetcher;
File.WriteAllBytes(FilePath, fetcher.bytes);
Application.OpenURL("file:///" + FilePath);
}
So I'm checking if the file exists on the device in storage, and if not I am 'extracting' it from the app and writing it to storage. Then I try to open the local 'URL' with the file:/// standard (this Application.OpenURL("https://www.google.com"); does work successfully to open that URL in my mobile browser).
Is there a way in Unity to create an Intent or something to trigger this? The FilePath works on Windows, and is the same path I am writing the bytes to, so that should be correct... or am I missing something?
NOTE: this is also not working in iOS, but I thought I read in the Unity forums (can't find the link now) to Android being the only exception to the paths/URL thing... can anyone help me out with that, too?
If you are trying to open any document using Application.OpenURL then it won't work. Unity removed that support in latest versions. You can use AndroidOpenUrl.OpenUrl(documentUrl, dataType) to resolve this issue.
public void Example()
{
string dataType = "application/pdf";
string documentUrl = "/storage/emulated/0/Downloads/template.pdf";
AndroidOpenUrl.OpenUrl(documentUrl, dataType); // you can specify any MIME type when opening a file by explicitly specifying the dataType parameter
}
You will get a demo and installation steps of the package from the following repository.
https://github.com/codemaker2015/Unity-Android-Files-Opener
Android permissions changed in Nougat (v.7)
you won't be able to open by path only, the sistem was blocking it.
Just lower the target API level to 23 (Android 6.0 'Marshmallow').
you could just use this code as per this reference URL:
How to Open a PDF in an Android-Unity3D App?
void openPDF(){
string namePDF = "test";
TextAsset pdfTem = Resources.Load("PDFs/"+namePDF, typeof(TextAsset)) as TextAsset;
System.IO.File.WriteAllBytes(Application.persistentDataPath + "/"+namePDF+".pdf", pdfTem.bytes);
Application.OpenURL(Application.persistentDataPath+"/"+namePDF+".pdf");
}
So I'm trying to create a file and I'm getting
System.UnauthorizedAccessException: Access to the path "/DownloadJitters" is denied. I'm not sure if it's a permissions thing (I've tried a write to external storage in case but that didn't work) or something else. Also I'm trying to figure out a good place to write these files as I would like them not to be easily found. Any ideas? Here's the code as well :
public void favouriteList(MainActivity av, Ordering o, string favouriteName, string totalCost, JittersListView jlv)
{
//Checks Directory exists
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
{
Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
}
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt") == false)
{
var fav = File.Create(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt");
fav.Close();
string file = Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt";
string added = null;
int current = 0;
while (true)
{
if (current < jlv.Count)
{
JittersListItem jli = jlv[current];
added += jli.Top + "|" + jli.Bottom + "|" + jli.itemPic + "|" + jli.itemDes + System.Environment.NewLine;
current++;
}
else
{
break;
}
}
File.AppendAllText(file, favouriteName + "|" + totalCost + added);
}
else
{
new AlertDialog.Builder(av)
.SetMessage("Please use a different name, this one has been taken.")
.Show();
}
}
First of all add this permissions to you Manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Since Android 6.0 (API 23) you need also to request the permissions manually, add this code on your MainActivity.cs on your Xamarin.Android project:
if ((ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted)
|| (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted))
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, REQUEST);
}
Since Android 10 you may also need to add android:requestLegacyExternalStorage attribute to your Manifest like this:
<application android:requestLegacyExternalStorage="true" />
UPDATE
After doing all this maybe you still get the exception on Android 11 or greather If you try save a file on any path of the SDCARD, you can only use the EXTERNAL STORAGE and INTERNAL STORAGE paths of your App by default.
PRIVATE EXTERNAL STORAGE PATH OF YOUR APP:
Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath
PRIVATE INTERNAL STORAGE PATH OF YOUR APP:
System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)
If you need to access to any path of the SDCARD on Android 11 or greather, you should ask for manage all files access permission.
private void RequestStorageAccess()
{
if (!Environment.IsExternalStorageManager)
{
StartActivityForResult(new Intent(Android.Provider.Settings.ActionManageAllFilesAccessPermission), 3);
}
}
This will pop up an Activity listing all the Apps that have access to all file access permission, the user should tap your App and enable the permission.
But in order to make your App appear there, you should add this permission to your manifest:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
And you also need to make sure that you are compiling your App with Android 11 or greather.
Here you can see the permission Activity:
For some reason I was able to save files to documents folder without this permission, but after deleting the files I was not able to create again the same files with the same name, also I was not able to list all the files, this is why this pop-up is needed even to use the documents directory.
Use manage all files permission only in case that you really need
If you want to publish your App in Google Play with the "Manage All Files Permission", you will have to Fill a "Permissions Declaration Form" on Google Play, because it is considered a
"high-risk or sensitive permissions" here is more information, and Google should approve it:
https://support.google.com/googleplay/android-developer/answer/9214102?hl=en#zippy=
When to ask for this permission:
https://support.google.com/googleplay/android-developer/answer/10467955?hl=en
Ok I Fixed it by changing the saving location to
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)
Don't ask me why that worked when they need the same permissions but it did.
Xamarin.Forms (Android solution)
MainActivity.cs
For apps that target Android 5.1(API level 22) or lower, there is nothing more that needs to be done.
Apps that will run on Android 6.0(API 23 level 23) or higher should ask Run time permission checks.
protected override void OnCreate(Bundle bundle)
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
if (!(CheckPermissionGranted(Manifest.Permission.ReadExternalStorage) && !CheckPermissionGranted(Manifest.Permission.WriteExternalStorage)))
{
RequestPermission();
}
}
LoadApplication(new App());
}
private void RequestPermission()
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, 0);
}
public bool CheckPermissionGranted(string Permissions)
{
// Check if the permission is already available.
if (ActivityCompat.CheckSelfPermission(this, Permissions) != Permission.Granted)
{
return false;
}
else
{
return true;
}
}
This looks like a copy and paste error - you should learn to refactor common code and expressions into one value and reuse it.
//Checks Directory exists
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
{
Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
}
Let's assume Android.OS.Environment.DirectoryDownloads has the value /Downloads. Now go through the code line by line (you should really do this with a debugger):
File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt")
The parameter value here will be "/Downloads/Jitters/FavouritesListAdded.txt" - OK
Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
There's no leading slash on the literal string here, so the value will be: /DownloadsJitters/FavouriteList - I'm guessing you probably meant it to be /Downloads/Jitters/FavouriteList.
Rather than making sure slashes are added to all 6 path expressions in your code - just create one variable with the path value and reuse it.
If you are still getting UnauthorizedAccessException for write or read file in Xamarin Android. I just written article to resolve it
http://bsubramanyamraju.blogspot.com/2019/12/resolved-unauthorizedaccessexception.html
Maybe someone knows a simple solution to my problem.
I do not know the entry of the file so it's not a static value.
It can be changed through the BizTalk gui and there we have a URI through the receiveport. But I do not believe it's accessible that easy. What I want to do is write out the full path as the filename. It works well with the messageID where the file is given a specific filepath name. But the Path-Name where the file was dropped is not working that well.
I keep getting this error :
Message:
Object reference not set to an instance of an object.
the message resource is present but the message is not found in the string/message table
-Does not say me much
Below you can see a snip from my code
internal static string UpdateMacroPathProperty(IBaseMessage baseMessage, string macroPathProperty, string macroDefsFile)
{
if (macroName == "MessageID")
{
contextPropertyValue = baseMessage.MessageID.ToString();
}
else if (macroName == "SourceFileName")
{
contextPropertyValue = Directory.GetCurrentDirectory();
}
}
This is an specific created pipeline. Has anyone encountered this problem or can point me in the right way.
I know that BizTalk has a built in function for this, BizTalk Server: List of Macros as the %SourceFileName% but I'm trying to save this as logs in a specific map structure so that it does not get processed.
It's adapter dependent; some adapters will use the FILE adapter's namespace even though they're not the file adapter, but this is the kind of logic that I've used in the past for this:
string adapterType = (string)pInMsg.Context.Read("InboundTransportType",
"http://schemas.microsoft.com/BizTalk/2003/system-properties");
string filePath = null;
if (adapterType != null)
{
if (adapterType == "FILE")
{
filePath = (string)pInMsg.Context.Read("ReceivedFileName",
"http://schemas.microsoft.com/BizTalk/2003/file-properties");
}
else if (adapterType.Contians("SFTP") && !adapterType.Contains("nsoftware"))
// nsoftware uses the FTP schema
{
filePath = (string)pInMsg.Context.Read("ReceivedFileName",
"http://schemas.microsoft.com/BizTalk/2012/Adapter/sftp-properties");
}
else if (adapterType.Contains("FTP"))
{
filePath = (string)pInMsg.Context.Read("ReceivedFileName",
"http://schemas.microsoft.com/BizTalk/2003/ftp-properties");
}
}
And then you can just fall back to the MessageID if you can't get the file path from any of these.