I'm sending multipart requests with retrofit and it works fine. but when my file name is contain Persian character ,my app crashes and i get this error:
java.lang.IllegalArgumentException: Unexpected char 0x62f at 35 in
Content-Disposition value: form-data; name="photo";
filename="دوچرخه.jpg"
this is how i send my multipart request:
File imageFile = new File(imagePath);
ProgressRequestBody fileBody = new ProgressRequestBody(imageFile, this);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("photo", imageFile.getName(), fileBody);
RetroInterface retroInterface = RetrofitClientInstance.getRetrofitInstance().create(RetroInterface.class);
Call<SendFileResponse> call = retroInterface.sendPhoto(token, myHashmap, filePart);
how can I fix this issue?!
My not perfect solution, becouse it can change some characters, is to put URLEncoder.encode(file.name, "utf-8") instead of plain name.
Just a suggestion though but as a workaround you try renaming the file to an id (timestamp / anything else) for the filename and add a name field that will contain the actual name if the file...
public class Object {
private String filename;
private File actualFile;
}
That way when retrieving later you can still always have reference to the file you want.
We can use addUnsafeNonAscii() method from Header.Builder class by which we can add a header with the specified name and value. Does validation of header names, allowing non-ASCII values. So we can pass any language characters as an value including persian character too.
val fileName = "your file name with extention"
val reqFile = RequestBody.create(MediaType.parse("image/*"), File(imageDir))
val header = Headers.Builder()
header.addUnsafeNonAscii("Content-Disposition", "form-data; name=\"image\"; filename=\"$fileName")
val body = MultipartBody.Part.create(header.build(), reqFile)
myApi.uploadItem(body)
Related
I have a list like
[{
Imgstr:"obj as string",
Ingfile:file
},{
Imgstr:"obj as string",
Ingfile:file
}
]
How can I upload them as multipart x if it is a single obj I am sending as multipart body and file
try going to this link and I'll include some of it here:
If this is your first uploading files with Retrofit tutorial, you should visit our uploading files with Retrofit and uploading multiple files tutorials.
In the previous tutorials, we've used various upload options in the FileUploadService class:
public interface FileUploadService {
// previous code for single file uploads
#Multipart
#POST("upload")
Call<ResponseBody> uploadFile(
#Part("description") RequestBody description,
#Part MultipartBody.Part file);
// previous code for multiple files
#Multipart
#POST("upload")
Call<ResponseBody> uploadMultipleFiles(
#Part("description") RequestBody description,
#Part MultipartBody.Part file1,
#Part MultipartBody.Part file2);
}
The second option let's you upload multiple files, but you always have to specify in advance how many. This is difficult when your app doesn't have a fixed number of files and it can vary depending on the use case or user input.
Upload a Dynamic Amount of Files
The solution for this problem is to pass a List or Array of MultipartBody.Part objects. Retrofit and OkHttp will then build an appropriate multipart request with all files. Java arrays or lists allow you to freely add files as required.
Endpoint Declaration
You know the theory, so it's time to look at an example. As always, we'll start with describing the endpoint interface. Of course, this depends on your backend. Make sure your API can handle a random amount of files!
public interface FileUploadService {
#Multipart
#POST("upload")
Call<ResponseBody> uploadMultipleFilesDynamic(
#Part("description") RequestBody description,
#Part List<MultipartBody.Part> files);
}
In the previous examples we carried a description with every request. We'll keep it to show you how it would work, but of course it's only a single description for a lot of files. If you need to also send a dynamic amount of other information, you should check out our tutorial on #PartMap.
The second part of the implementation is using the new endpoint declaration and passing some files. We'll reuse the helper methods from our previous tutorials to simplify creating the necessary multiparts:
#NonNull
private RequestBody createPartFromString(String descriptionString) {
return RequestBody.create(
okhttp3.MultipartBody.FORM, descriptionString);
}
#NonNull
private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {
// https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java
// use the FileUtils to get the actual file by uri
File file = FileUtils.getFile(this, fileUri);
// create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(
MediaType.parse(getContentResolver().getType(fileUri)),
file
);
// MultipartBody.Part is used to send also the actual file name
return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
}
Finally, we'll put everything together to build the upload request:
Uri photoUri = ... // get it from a file chooser or a camera intent
Uri videoUri = ... // get it from a file chooser or a camera intent
// ... possibly many more file uris
// create list of file parts (photo, video, ...)
List<MultipartBody.Part> parts = new ArrayList<>();
// add dynamic amount
if (photoUri != null) {
parts.add(prepareFilePart("photo", photoUri));
}
if (videoUri != null) {
parts.add(prepareFilePart("video", videoUri));
}
// ... possibly add more parts here
// add the description part within the multipart request
RequestBody description = createPartFromString("hello, this is description speaking");
// create upload service client
FileUploadService service = ServiceGenerator.createService(FileUploadService.class);
// finally, execute the request
Call<ResponseBody> call = service.uploadMultipleFilesDynamic(description, parts);
call.enqueue(...);
All of the steps above should be fairly familiar to you. We simply create a part for each file and the description. After everything is put together, we can create a new Call object from our FileUploadService and execute the request as usual.
I've created in Xamarin Forms for iOS a HttpClient function to send a picture from the device to my server. The core function is
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(fileBytes);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fName
};
fileContent.Headers.ContentDisposition.Parameters
.Add(new NameValueHeaderValue("userId", UserId.ToString()));
content.Add(fileContent);
using (var client = new HttpClient()) {
client.DefaultRequestHeaders.Add("authenticationToken", SyncData.Token);
HttpResponseMessage response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
// more code
}
}
I'm using System.Net.Http. I tried to use the same function for a project in Android but surprisingly it doesn't work. The problem is in the header: if I inspect fileContent I can see every keys but for webapi on the server FileName is not received.
After some logs, I changed this function adding more client.DefaultRequestHeaders like
client.DefaultRequestHeaders.Add("FileName", fName);
Now the webapi receives FileName param.
Now my question is: what did I wrong?
Personally, I use the Add method on MultipartFormDataContent that accepts a filename.
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(fileBytes);
...
// Use the overload Add method which accepts a file name
content.Add(fileContent, "FileName", fName);
...
I'm not sure if this will solve your problem or not, but it works for me.
I need to load my json file in my device. It is working perfectly fine in Unity Editor but when I transferred it to my device, json isn't there. Currently using this code for adding data in my json file:
TextAsset file = Resources.Load("Products") as TextAsset;
string jsonString = file.ToString ();
Debug.Log (jsonString);
if (jsonString != null)
{
jsonParser data = JsonUtility.FromJson<jsonParser>(jsonString);
data.products.Add(product);
Debug.Log(data.products[1].code);
jsonString = JsonUtility.ToJson(data);
Debug.Log(jsonString);
}
And this for retrieving:
TextAsset file = Resources.Load("Products") as TextAsset;
string json = file.ToString ();
//string path = Application.streamingAssetsPath + "/Products.json";
string path = Application.persistentDataPath + "/Resources/Products.json";
string jsonString = File.ReadAllText (path);
jsonParser data = JsonUtility.FromJson<jsonParser>(jsonString);
It can be use in Unity editor but not in my android device. What seems to be the problem? Very big thanks
As explained here, you can't read file from the Resources folder with anything other than Resources.Load. You must use Resources.Load to read anything placed in the Resources folder.
It doesn't matter if it works in the Editor or not the code below that reads from the Resources folder should not work in any build:
string path = Application.persistentDataPath + "/Resources/Products.json";
string jsonString = File.ReadAllText (path);
You don't seem to understand when the Resources folder should be used. This is used to store files that does not need to be changed during run-time. For example, your textures, sound, video files and prefabs. These are not meant to be modified during run-time Also, using it to store default value for a text file is fine.
What need to do is to read the file with Resources.Load("Products") then store it in a string. After you modify it, save to another directory with
Save:
string tempPath = Path.Combine(Application.persistentDataPath + "/data/", dataFileName + ".txt");
File.WriteAllBytes(tempPath, modifiedJson);.
Load:
string tempPath = Path.Combine(Application.persistentDataPath + "/data/", dataFileName + ".txt");
jsonByte = File.ReadAllBytes(tempPath);
I made a generic class in another question, to handle that easily so you that you don't even need to use the Resources folder or read the file manually. It handles all the errors too.
From your question, your data is stored in jsonParser class. You should rename that class to actually reflect what you are storing such as PlayerInfo or ProductInfo.
Grab the DataSaver class from here. With your current jsonParser data, you can save and load with the code sample below:
Save:
jsonParser products = new jsonParser();
DataSaver.saveData(products, "Products");
Load:
jsonParser loadedProducts = DataSaver.loadData<jsonParser>("Products");
Delete:
DataSaver.deleteData("Products");
Very easy. That should work on any Platform.
Another simple variant, if you ONLY want to get JSON's text from Resourse:
1.⠀You have file in Resource/Jsons folder, example "simple.json"
2.⠀You use this code:
TextAsset file = Resources.Load("Jsons/simple") as TextAsset;
string json = file.text;
3. Now, you have JSON in string variable! 4. Thats all! Glory to Robots!
I'm developing an app that will send a file using HTTP server (with nanoHTTPD) to another device by typing the sender's IP:port. The transferring is working correctly, but I'm not able to receive the right file name of the sent file (its being named as 'default', without any extension, by the receiver's browser). Here's my code for the HTTP server:
private class WebServer extends NanoHTTPD {
public WebServer()
{
super(8080);
}
#Override
public Response serve(String uri, Method method,
Map header, Map parameters,
Map files) {
//receive my file's path from an intent
Intent intent = getIntent();
String filename = intent.getStringExtra(MainActivity.FILENAME);
FileInputStream file = null;
try {
file = new FileInputStream(filename);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return new NanoHTTPD.Response(Status.OK, "/", file);
}
I thought I could fix it using FileInputStream(new File(String path, String name)) but it still doesn't work right and it still give me a 'default' file name with 0 byte file size.
Can anyone give me some idea how can I get the right file name from the HTTP server? Hope someone can help me here. Thanks!
It depends on what your server (script) accepts, when sending you should add headers with name of your file, this can be:
Content-Disposition: attachment; filename="fname.ext"
example from http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html "19.5.1 Content-Disposition"
I would like to store some strings in a simple .txt file and then read them, but when I want to encode them using Base64 it doesn't work anymore: it writes well but the reading doesn't work. ^^
The write method:
private void write() throws IOException {
String fileName = "/mnt/sdcard/test.txt";
File myFile = new File(fileName);
BufferedWriter bW = new BufferedWriter(new FileWriter(myFile, true));
// Write the string to the file
String test = "http://google.fr";
test = Base64.encodeToString(test.getBytes(), Base64.DEFAULT);
bW.write("here it comes");
bW.write(";");
bW.write(test);
bW.write(";");
bW.write("done");
bW.write("\r\n");
// save and close
bW.flush();
bW.close();
}
The read method :
private void read() throws IOException {
String fileName = "/mnt/sdcard/test.txt";
File myFile = new File(fileName);
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader inBuff = new BufferedReader(new InputStreamReader(fIn));
String line = inBuff.readLine();
int i = 0;
ArrayList<List<String>> matrice_full = new ArrayList<List<String>>();
while (line != null) {
matrice_full.add(new ArrayList<String>());
String[] tokens = line.split(";");
String decode = tokens[1];
decode = new String(Base64.decode(decode, Base64.DEFAULT));
matrice_full.get(i).add(tokens[0]);
matrice_full.get(i).add(tokens[1]);
matrice_full.get(i).add(tokens[2]);
line = inBuff.readLine();
i++;
}
inBuff.close();
}
Any ideas why?
You have a couple of errors in your code.
First a couple of notes on your code:
When posting here, attaching a SSCCE helps others to debug your code. This is not a SSCEE because it doesn't compile. It lacks several defined variables, so one must guess what you really mean. Also you have pasted close-comment token in your code: */ but there is no one start-comment token.
Catching and just suppressing exceptions (like in catch-block in read method) is really bad idea unless you really know what you're doing. What it does most of the time is hide the potential problems from you. At least write the stacktrace of an exception is a catch block.
Why don't you just debug it, check what exactly outputs to the destination file? You should learn how to do that because that will speed up your development process, especially for larger projects with hard-to-catch problems.
Back to the solution:
Run the program. It throws an exception:
02-01 17:18:58.171: E/AndroidRuntime(24417): Caused by: java.lang.ArrayIndexOutOfBoundsException
caused by line here:
matrice_full.get(i).add(tokens[2]);
inspecting the variable tokens reveals that it has 2 elements, not 3.
So lets open the file generated by the write method. Doing that shows this output:
here it comes;aHR0cDovL2dvb2dsZS5mcg==
;done
here it comes;aHR0cDovL2dvb2dsZS5mcg==
;done
here it comes;aHR0cDovL2dvb2dsZS5mcg==
;done
Note line breaking here. This is because the Base64.encodeToString() appends additional newline at the end of the encoded string. To generate a one single line, without extra newlines, add Base64.NO_WRAP as the second parameter like this:
test = Base64.encodeToString(test.getBytes(), Base64.NO_WRAP);
Note here, you must delete file that was created earlier as it has improper line breaking.
Run the code again. It now creates a file with the proper contents:
here it comes;aHR0cDovL2dvb2dsZS5mcg==;done
here it comes;aHR0cDovL2dvb2dsZS5mcg==;done
Printing the output of matrice_full now gives:
[
[here it comes, aHR0cDovL2dvb2dsZS5mcg==, done],
[here it comes, aHR0cDovL2dvb2dsZS5mcg==, done]
]
Note that you're not doing anything with the value in decode variable in your code, hence the second element is the Base64 representation of that value which is read from the file.