Upload file and text with Method POST on iOS - iphone

I'm trying to send a JSON to the web service with a file and some text, but the server answers as invalid.
I have been looking through this for 3 days and can't get what's wrong, so I ask you for help.
This is the web form to talk to the web service:
<html>
<head>
<title>Nova Dica Form</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form action="http://serveraddress.com/newtip" method="post" enctype="multipart/form-data">
<input type="hidden" name="textoDica" value="Ola teste dica">
<input type="hidden" name="idUsuario" value="2">
<input type="hidden" name="recomendou" value="true">
<input type="hidden" name="idTags" value="2">
<input type="hidden" name="idMarcas" value="1">
<input type="hidden" name="idLocais" value="">
<input type="hidden" name="idCategorias" value="">
<input type="hidden" name="idImpressoes" value="">
<input name="foto1" type="file">
<input name="foto2" type="file">
<input name="foto3" type="file">
<br>
<input type="submit" value="Upload">
</form>
</body>
</html>
And this is what the php developer sent me:
POST /newtip
Host: serveraddress.com
Content-Type: multipart/form-data
Representation:
Content-Disposition: form-data; name="textoDica"
texto-dica
Content-Disposition: form-data; name="foto1"
Content-Type: image/jpeg, image/png
Content-Transfer-Encoding: binary
foto1
Content-Disposition: form-data; name="foto2"
Content-Type: image/jpeg, image/png
Content-Transfer-Encoding: binary
foto2
Content-Disposition: form-data; name="foto3"
Content-Type: image/jpeg, image/png
Content-Transfer-Encoding: binary
foto3
Content-Disposition: form-data; name="recomendou";
recomendou
Content-Disposition: form-data; name="idUsuario";
id-usuario
Content-Disposition: form-data; name="idTags";
id-tag
Content-Disposition: form-data; name="idMarcas";
id-marca
Content-Disposition: form-data; name="idLocais";
id-local
Content-Disposition: form-data; name="idCategorias";
id-categoria
Content-Disposition: form-data; name="idImpressoes";
id-Impressao
Response
Content-Type: application/json
Representation:
{ "fotoDica" : "http://s3.amazonaws.com/cooltips/dicas/f23f89as.jpg",
"horarioDica" : 1372711615696,
"idDica" : 21,
"recomendado" : 1,
"textoDica" : "TesteDica1"
}
So, based on that, I did this in my code (with some content I found here at stackoverflow:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://serveraddress.com/newtip"]]];
[request setHTTPMethod:#"POST"];
// Header
NSString *boundary = #"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
// Body
NSMutableData *body = [NSMutableData data];
// Dica/Tip
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"textoDica\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Teste de texto blablabla." dataUsingEncoding:NSUTF8StringEncoding]];
// Usuario
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"idUsuario\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"72" dataUsingEncoding:NSUTF8StringEncoding]];
// Recomendou
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"recomendou\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"true" dataUsingEncoding:NSUTF8StringEncoding]];
// Tags
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"idTags\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"aaaaaa;bbbbbb;cccccc" dataUsingEncoding:NSUTF8StringEncoding]];
// Marca
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"idMarcas\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"3" dataUsingEncoding:NSUTF8StringEncoding]];
// Local
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"idLocais\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"4" dataUsingEncoding:NSUTF8StringEncoding]];
// Categorias
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"idCategorias\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"1" dataUsingEncoding:NSUTF8StringEncoding]];
// Impressões
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"idImpressoes\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"bom;alegre" dataUsingEncoding:NSUTF8StringEncoding]];
// Fotos / Image Files
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Disposition: form-data; name=\"foto1\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: file/jpeg\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Transfer-Encoding: binary\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
NSData *imageData = UIImageJPEGRepresentation(_fotoArray[0], 0.40);
[body appendData:[NSData dataWithData:imageData]];
// Final Boundary
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Set the body
[request setHTTPBody:body];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSLog(#"%#", returnString);
I always get this answer from the server:
{"idDica":0,"fotoDica":null,"horarioDica":null,"textoDica":null,"recomendado":0}
But the right one should be something like this:
{"idDica":91,"fotoDica":"http://serveraddress.com/tips/91/6nyIZA8MPJ.png","horarioDica":1372960438226,"textoDica":"Hello","recomendado":1}
Can anyone have a clue on what am I doing wrong?
Thanks
EDIT: This is the web service code (out of my knowledge):
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public DicaRest uploadFile(
#FormDataParam("foto1") InputStream uploadedInputStream1,
#FormDataParam("foto1") FormDataContentDisposition fileDetail1,
#FormDataParam("foto2") InputStream uploadedInputStream2,
#FormDataParam("foto2") FormDataContentDisposition fileDetail2,
#FormDataParam("foto3") InputStream uploadedInputStream3,
#FormDataParam("foto3") FormDataContentDisposition fileDetail3,
#FormDataParam("textoDica") String textoDica,
#FormDataParam("idUsuario") long idUsuario,
#FormDataParam("recomendou") boolean recomendou,
#FormDataParam("idTags") String idTags,
#FormDataParam("idMarcas") String idMarcas,
#FormDataParam("idLocais") String idLocais,
#FormDataParam("idCategorias") String idCategorias,
#FormDataParam("idImpressoes") String idImpressoes)
{
if(idTags == null)
idTags = "";
if(idMarcas == null)
idMarcas = "";
if(idLocais == null)
idLocais = "";
if(idCategorias == null)
idCategorias = "";
if(idImpressoes == null)
idImpressoes = "";
EntityManager em = EntityFactory.getEntityManager("RestCTPU");
em.getTransaction().begin();
DicaRest dicaRest = new DicaRest();
Upload upload = new Upload();
List<File> pathfoto = new ArrayList<File>();
Dica novaDica = new Dica();
int i = 0;
try {
novaDica.setTextoDica(textoDica);
novaDica.setIdUsuario(new Usuario(idUsuario));
novaDica.setRecomendado(recomendou);
novaDica.setDataCadastro(new Date());
novaDica.setEstadoHabilitado(true);
novaDica = em.merge(novaDica);
//LINUX AMI
String pathLocal = "/var/lib/tomcat7/webapps/uploadtemp/"+novaDica.getIdDica() + "/";
//WINDOWS
//String pathLocal = "C:/teste/"+"dicas/"+novaDica.getIdDica() + "/";
String pathAlvo = "dicas/"+novaDica.getIdDica() + "/";
(new File(pathLocal)).mkdirs();
if (fileDetail1.getFileName().length() > 0) {
pathfoto.add( new File(pathLocal+Utilidades.getRandomPass(10)+fileDetail1.getFileName().substring(fileDetail1.getFileName().lastIndexOf("."))));
saveToFile(uploadedInputStream1, pathfoto.get(i).getAbsolutePath());
i++;
}
if (fileDetail2.getFileName().length() > 0) {
pathfoto.add( new File(pathLocal +Utilidades.getRandomPass(10)+fileDetail1.getFileName().substring(fileDetail1.getFileName().lastIndexOf("."))));
saveToFile(uploadedInputStream2, pathfoto.get(i).getAbsolutePath());
i++;
}
if (fileDetail3.getFileName().length() > 0) {
pathfoto.add( new File(pathLocal+Utilidades.getRandomPass(10)+fileDetail1.getFileName().substring(fileDetail1.getFileName().lastIndexOf("."))));
saveToFile(uploadedInputStream3, pathfoto.get(i).getAbsolutePath());
i++;
}
List<Tag> tags = new ArrayList<Tag>();
if(idTags.length() > 0) {
String[] tgs = idTags.split(";");
for(String tag : tgs) {
Tag nTag = new Tag(Long.valueOf(tag));
tags.add(nTag);
}
}
novaDica.setTagList(tags);
List<Alvo> alvos = new ArrayList<Alvo>();
if(idMarcas.length() > 0) {
String[] marcas = idMarcas.split(";");
for(String marca : marcas) {
Alvo nAlvo = new Alvo(Long.valueOf(marca));
alvos.add(nAlvo);
}
}
if(idLocais.length() > 0) {
String[] locais = idLocais.split(";");
for(String local : locais) {
Alvo nAlvo = new Alvo(Long.valueOf(local));
alvos.add(nAlvo);
}
}
novaDica.setAlvoList(alvos);
List<CategoriaImpressao> categoriaImpressaoList = new ArrayList<CategoriaImpressao>();
if(idCategorias.length() > 0) {
String[] categorias = idCategorias.split(";");
for(String categoria : categorias) {
CategoriaImpressao nCat = new CategoriaImpressao(Long.valueOf(categoria));
categoriaImpressaoList.add(nCat);
}
}
if(idImpressoes.length() > 0) {
String[] impressoes = idImpressoes.split(";");
for(String impressao : impressoes) {
CategoriaImpressao nImpress = new CategoriaImpressao(Long.valueOf(impressao));
categoriaImpressaoList.add(nImpress);
}
}
novaDica.setCategoriaImpressaoList(categoriaImpressaoList);
//String uploadedFileLocation = fileDetail1.getFileName();
// save it
List<Foto> fts = new ArrayList<Foto>();
for(String foto : upload.doUpload(pathfoto, pathAlvo)) {
Foto ft = new Foto();
ft.setUrlFoto(foto);
ft.setIdDica(novaDica);
ft.setDataCadastro(new Date());
ft.setEstadoHabilitado(true);
fts.add(ft);
}
novaDica.setFotoList(fts);
em.persist(novaDica);
em.getTransaction().commit();
try {
deleteDir(new File(pathLocal));
} catch (Exception e){}
dicaRest.setFotoDica(fts.get(0).getUrlFoto());
dicaRest.setHorarioDica(new Date());
dicaRest.setIdDica(novaDica.getIdDica());
dicaRest.setRecomendado(recomendou);
dicaRest.setTextoDica(textoDica);
} catch (Exception e) {
System.out.println(e);
} finally {
em.close();
em.getEntityManagerFactory().close();
}
return dicaRest;
}

I'd suggest trying specifying the upload filename, too, e.g.:
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"%#\"\r\n", fieldName, filename] dataUsingEncoding:NSUTF8StringEncoding]];
You also use a Content-Type of file/jpeg, but the spec from the developer says it should be image/jpeg or image/png. So you might want to use:
[body appendData:[#"Content-Type: image/jpeg\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
If neither of those do it, I notice that your values don't exactly match the HTML example, exactly, and given that we don't know what the validation rules are, I'd suggest you use precisely the same values (e.g. idTags is 2 in the HTML, but aaaaaa;bbbbbb;cccccc in your Objective-C). But we have no way of knowing what validation the PHP is performing, so eliminate the possibility that you're just not passing some simple validation rule. Make sure you're confident the problem is the forming of the request, and not the data you're passing in the request.
Hopefully your PHP developer can tell you precisely what validation is taking place.
Looking at your sample server source code, I am not familiar enough with this server code to diagnose it, but a couple of thoughts:
It seems like the references that grab the extension of fileDetail1 when working with fileDetail2 and fileDetail3 seems like it can't be right (what if they were different types?!?). Given that you're passing only one, that's unlikely to be the source of the problem, but it doesn't seem right.
It would suggest, though, that consistent with my earlier observation about supplying a filename, it's important to do so, and probably one with a jpg extension. I would encourage you to include a filename (even if it's just "test.jpg").
This code makes me wonder what happens when your request doesn't pass any foto2 or foto3. Hopefully it handles that gracefully, not throwing any exception, but I can't say.
I may be misreading this, but it looks like your idTags must be numbers separated by semicolons, not aaaaaa;bbbbbb;cccccc. See the references to Long.
It looks like the same is true with idImpressoes, that it should be numbers separate by semicolons, not bom;alegre.
It strikes me that point 4 and point 5 are the most immediate candidate issues. I'd make both of those numbers and see if that fixes it.

Related

NSData appendData: UIImageJPEGRepresentation() returns nil

This has had me scratching for quite some time.
I am preparing a UIImage to post as part of a HTTP request. I am doing this with the following code:
[postData appendData:[[NSString stringWithFormat:#"--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[[NSString stringWithFormat:#"Content-Disposition: attachment; name=\"q%#\"; filename=\".jpg\"\r\n" , key] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[#"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[NSData dataWithData:UIImageJPEGRepresentation(image, 1.0)]];
[postData appendData:[#"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
The problem I am having is that postData is null after running these lines.
Using NSLog I found out that [postData appendData:[NSData dataWithData:UIImageJPEGRepresentation(image, 1.0)]]; is the line which is causing this to happen. I then went on to see if the image was a the problem. UIImageJPEGRepresentation(image, 1.0) outputs a mass of data to the console and adding image to a UIImageView shows the .jpg I was expecting.
I am absolutely bamboozled. Please help >.<
S
EDIT
After a bit of a debacle below I have realised that it is definitely posting the image
.
NSLog(#"postData = %#",[postData length]);
shows 280861 (bytes?) but a PHP file with <?php print_r($_FILES); ?> returns Array {}.
UIImageJPEGRepresentation(image, 1.0) returns a NSData object that can't be turned into a NSString. A jpeg does not consist of valid characters. And arbitrary data can't be converted to a valid string.
Even if UIImageJPEGRepresentation(image, 1.0) returns nil, the [NSData dataWithData:] call makes sure that the data you append to postData is not nil. [NSData dataWithData:nil]; returns an empty NSData instance, not nil.
Regarding your answer. nil is not a valid string encoding. The compiler should show a warning and you should see a warning in the console "Incorrect NSStringEncoding value 0x0000 detected. Assuming NSASCIIStringEncoding. Will stop this compatiblity mapping behavior in the near future.".
You can't convert postData back into a NSString. If you want to check that the image is part of the NSData object you could look at the length of postData.
Resolved with:
[postData appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\";\r\nfilename=\"%#.jpeg\"\r\nContent-Type: image/jpeg\r\n\r\n", key,key] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[NSData dataWithData:imageData]];
[postData appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
The first newline after my already included data seems to have resolved this
How did you tell postData was null? Please note that you can't convert it to UTF8 string using some category/extension as below.
extension NSData {
func to_string(encoding:UInt = NSUTF8StringEncoding) -> String? {
return NSString(data:self, encoding:encoding) as? String
}
}

Bad request -400 status code

I am using http post method and passing base64econded string into xml. But it gives me 400 in response. XMl tag structure is valid. When I removed this base64encoded string from xml it works perfectly. But including it gives error(400). Would base64encoded string may contains any character that make my request invalid ? Any help is welcome.
This may be caused by your boundary definitions not being 100% correct, eg. using wrong
Content-type: multipart/form-data, boundary=XXboundaryXX
instead of the correct
Content-type: multipart/form-data; boundary=XXboundaryXX
It may also be your boundary delimiters in the body that are not using correct syntax, eg. using wrong
--XXboundaryXX\r\n
instead of the correct
\r\n--XXboundaryXX\r\n
The following snippet should be correct for posting xml data, even for som nitty gritty older apache servers:
[request setValue:USER_AGENT forHTTPHeaderField:#"User-Agent"];
[request setTimeoutInterval: 15];
[request setHTTPMethod:#"POST"];
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request setValue:contentType forHTTPHeaderField:#"Content-type"];
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[#"Content-Disposition: form-data; name=\"xmlstring\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postBody];
I have previously discovered error 400 if these delimiters were not 100% correct.
Check your base64encoder. You can do it here
I'm using solution from this discussion.
If it's all right with your encoder, I suppose you're incorrect paste it into your xml (maybe with slashes or other 'bad' symbols)

To upload the image file to a web form

I do have some image files(.png files) in document directory(iPhone). I am viewing web forms(.aspx) on the UIWebView. when I click on the submit button of the form, i just want to attach the image file to the form and wants to send that file to the web server along with the web form.
The thing I m suffering from is, i am not getting how to attach an image file to the web form and how to submit those two.
Kindly help me out of this. I am not getting any of the hint how to do it.
Thanking you....
You will need to modify these examples to work for you, but they are working examples.
Objective-C
MyViewController.m:
- (void) upload
{
NSString *urlString = #"http://www.yourwebsite.com/uploads/upload.php";
NSData *data = /* turn your png into NSData */
// setting up the request object now
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSString *boundary = [NSString stringWithString:#"---------------------------14737809831466499882746641449"];
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
/* now lets create the body of the post */
NSString *content = [NSString stringWithFormat:#"Content-Disposition: form-data; name=\"userfile\"; filename=\"%#.png\"\r\n",#"yourPng"];
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:content] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:#"Content-Type: text/plain\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:data]];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[request setHTTPBody:body];
// now lets make the connection to the web
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
}
PHP
upload.php:
<?php
//assuming upload.php and upload folder are in the same dir
$uploaddir = 'uploads/';
$file = basename($_FILES['userfile']['name']);
$uploadfile = $uploaddir . $file;
$product = $_GET[product];
if (is_uploaded_file($_FILES['userfile']['tmp_name']))
{
echo "PNG uploaded. \r\n";
} else {
echo "PNG not uploaded. \r\n";
}
if ($_FILES['userfile']['size']> 300000) //Limiting image at 300K
{
exit("Your file is too large.");
}
// Add support here for PNG files:
if ((!($_FILES['userfile']['type'] == "text/plain")) && //Also allowing
(!($_FILES['userfile']['type'] == "text/plist")) && //plist files
(!($_FILES['userfile']['type'] == "text/html"))) //HTML files
{
exit("Incorrect file type. " . $_FILES['userfile']['type'] . " is the file type you uploaded.");
}
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
$postsize = ini_get('post_max_size'); //Not necessary, I was using these
$canupload = ini_get('file_uploads'); //server variables to see what was
$tempdir = ini_get('upload_tmp_dir'); //going wrong.
$maxsize = ini_get('upload_max_filesize');
echo "http://www.yourwebsite.com/uploads/{$file}" . "\r\n" . $_FILES['userfile']['size'] . "\r\n" . $_FILES['userfile']['type'] ;
}
?>
if you are trying to upload file from iphone safari.
you are screwed, there is no support to upload file from iPhone safari.
<input type="FILE"/> will be disable.
Solved my own problem! Last echo line is what needed to be adjusted!
<?php
$uploaddir = './'; //Uploading to same directory as PHP file ./
$file = basename($_FILES['userfile']['name']);
$uploadFile = $file;
$randomNumber = rand(0, 99999);
$newName = $uploadDir . $randomNumber . $uploadFile;
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
} else {
echo "Temp file not uploaded. \r\n";
}
if ($_FILES['userfile']['size']> 3000000000000000) {
exit("Your file is too large.");
}
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $newName)) {
$postsize = ini_get('post_max_size'); //Not necessary, I was using these
$canupload = ini_get('file_uploads'); //server variables to see what was
$tempdir = ini_get('upload_tmp_dir'); //going wrong.
$maxsize = ini_get('upload_max_filesize');
echo "http://localhost:8888/wouldYa/images/{$randomNumber}{$file}" . "\r\n";
}
?>
Now I am in the mist of saving the Str in Xcode, to another PHP file to insert the filename to that user's section in my table, specifically the display picture section, this should be no hard and implemented :-)
If you are interested in my full source code, please let me know, I know it is not that secure yet but soon it will.

Upload Photo from iPhone to Rails server with attachment_fu

I think I've been able to send a photo from the iPhone to Rails using the Multipart format, but once the photo gets to the Rails server, I can't figure out how to save it correctly. I think the file shows up in the params on the Rails server as "userfile" at the end of the Parameters below when iphone_photo_to_website is called:
Parameters: {"action"=>"iphone_photo_to_website","id"=>"8A39FA8F_1ADD_52AB_8387_E1C5CFC4AB2D", "controller"=>"users", "userfile"=>#<File:/tmp/RackMultipart30053-0>}
I send the photo from the iPhone using the following Objective-C code from the following SO question:
How can I upload a photo to a server with the iPhone?
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"logo1" ofType:#"png"];
NSData *imageData = [NSData dataWithContentsOfFile:imagePath];
NSString *urlString = [NSString stringWithFormat:#"%#/users/iphone_photo_to_website/%#",
serverString, UID];
NSMutableURLRequest *imageRequest = [[NSMutableURLRequest alloc] init] ;
[imageRequest setURL:[NSURL URLWithString:urlString]];
[imageRequest setHTTPMethod:#"POST"];
NSString *boundary = [NSString stringWithString:#"---------------------------14737809831466499882746641449"];
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[imageRequest setValue:contentType forHTTPHeaderField: #"Content-Type"];
NSMutableData *body = [NSMutableData dataWithCapacity:[imageData length] + 512];
[body appendData:[[NSString stringWithFormat:#"--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:#"Content-Disposition: form-data; name=\"userfile\"; filename=\"logo1.png\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:#"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imageData]];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[imageRequest setHTTPBody:body];
theConnection = [[NSURLConnection alloc] initWithRequest:imageRequest
delegate:self];
On the Rails server the photo migration in db/migrate looks like:
class CreatePhotos < ActiveRecord::Migration
def self.up
create_table :photos do |t|
t.column :user_id, :integer
t.column :title, :string
t.column :body, :text
t.column :created_at, :datetime
# the following are for attachment_fu
t.column :photo, :binary
t.column :content_type, :string
t.column :filename, :string
t.column :size, :integer
t.column :parent_id, :integer
t.column :thumbnail, :string
t.column :width, :integer
t.column :height, :integer
end
end
def self.down
drop_table :photos
end
end
On the Rails server I have a Photo model app/models/photo.rb with the following code:
class Photo < ActiveRecord::Base
has_attachment :storage => :file_system,
:resize_to => '640x480',
:thumbnails => { :thumb => '160x120', :tiny => '50>' },
:max_size => 5.megabytes,
:content_type => :image,
:processor => 'Rmagick'
validates_as_attachment
belongs_to :user
end
And the Rails controller has the following code:
def iphone_photo_to_website
#udid = params[:id]
#userfile = params[:userfile]
#photo_params = { :user_id => #user.id,
:photo => #userfile,
:content_type => 'image',
:filename => #userfile.original_path,
:uploaded_data => #userfile
}
image = Photo.new(#photo_params)
image.save
end
When "image.save" executes, it returns "false". Does anyone know how I can find what error is causing image.save to return false? Or does anyone know how I can save the photo correctly in the database?
I was finally able to save the image to the database using the following code:
#userfile = params[:userfile]
image = Photo.new(:uploaded_data => params[:userfile])
image.user_id = #user.id
image.photo = #userfile
image.width = 105
image.height = 104
basename = File.basename(image.filename).gsub(/[^\w._-]/, '')
image.content_type = "image/" + basename.split("\.")[1]
image.save
When the image is created as a Photo.new using the uploaded_data field, the image.filename gets set.
I also had to remove the following line from the Objective-C code on the iPhone to get things to work.
[body appendData:[[NSString stringWithString:#"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];

File Upload to HTTP server in iphone programming

Can anyone provide me some links or examples to upload files to the HTTP server using
iphone APIs.
The code below uses HTTP POST to post NSData to a webserver. You also need minor knowledge of PHP.
NSString *urlString = #"http://yourserver.com/upload.php";
NSString *filename = #"filename";
request= [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
NSMutableData *postbody = [NSMutableData data];
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"userfile\"; filename=\"%#.jpg\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithString:#"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[NSData dataWithData:YOUR_NSDATA_HERE]];
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postbody];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSLog(#"%#", returnString);
ASIHTTPRequest is a great wrapper around the network APIs and makes it very easy to upload a file. Here's their example (but you can do this on the iPhone too - we save images to "disk" and later upload them.
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setPostValue:#"Ben" forKey:#"first_name"];
[request setPostValue:#"Copsey" forKey:#"last_name"];
[request setFile:#"/Users/ben/Desktop/ben.jpg" forKey:#"photo"];
I used ASIHTTPRequest a lot like Jane Sales answer but it is not under development anymore and the author suggests using other libraries like AFNetworking.
Honestly, I think now is the time to start looking elsewhere.
AFNetworking works great, and let you work with blocks a lot (which is a great relief).
Here's an image upload example from their documentation page on github:
NSURL *url = [NSURL URLWithString:#"http://api-base-url.com"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:#"avatar.jpg"], 0.5);
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path:#"/upload" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:imageData name:#"avatar" fileName:#"avatar.jpg" mimeType:#"image/jpeg"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[httpClient enqueueHTTPRequestOperation:operation];
This is a great wrapper, but when posting to a asp.net web page, two additional post values need to be set:
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
//ADD THESE, BECAUSE ASP.NET is Expecting them for validation
//Even if they are empty you will be able to post the file
[request setPostValue:#"" forKey:#"__VIEWSTATE"];
[request setPostValue:#"" forKey:#"__EVENTVALIDATION"];
///
[request setFile:FIleName forKey:#"fileupload_control_Name"];
[request startSynchronous];
Try this.. very easy to understand & implementation...
You can download sample code directly here https://github.com/Tech-Dev-Mobile/Json-Sample
- (void)simpleJsonParsingPostMetod
{
#warning set webservice url and parse POST method in JSON
//-- Temp Initialized variables
NSString *first_name;
NSString *image_name;
NSData *imageData;
//-- Convert string into URL
NSString *urlString = [NSString stringWithFormat:#"demo.com/your_server_db_name/service/link"];
NSMutableURLRequest *request =[[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
//-- Append data into posr url using following method
NSMutableData *body = [NSMutableData data];
//-- For Sending text
//-- "firstname" is keyword form service
//-- "first_name" is the text which we have to send
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"%#\"\r\n\r\n",#"firstname"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"%#",first_name] dataUsingEncoding:NSUTF8StringEncoding]];
//-- For sending image into service if needed (send image as imagedata)
//-- "image_name" is file name of the image (we can set custom name)
[body appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:#"Content-Disposition:form-data; name=\"file\"; filename=\"%#\"\r\n",image_name] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[#"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imageData]];
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
//-- Sending data into server through URL
[request setHTTPBody:body];
//-- Getting response form server
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
//-- JSON Parsing with response data
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"Result = %#",result);
}
This isn't an alternative solution; rather a suggestion for Brandon's popular answer (seeing as though I don't have enough rep to comment on that answer). If you're uploading large files; you're probably going to get a mmap malloc exception on account of having to read the file into memory to post it to your server.
You can tweak Brandon's code by replacing:
[request setHTTPBody:postbody];
With:
NSInputStream *stream = [[NSInputStream alloc] initWithData:postbody];
[request setHTTPBodyStream:stream];
I thought I would add some server side php code to this answer for any beginners that read this post and are struggling to figure out how to receive the file on the server side and save the file to the filesystem.
I realize that this answer does not directly answer the OP's question, but since Brandon's answer is sufficient for the iOS device side of uploading and he mentions that some knowledge of php is necessary, I thought I would fill in the php gap with this answer.
Here is a class I put together with some sample usage code. Note that the files are stored in directories based on which user is uploading them. This may or may not be applicable to your use, but I thought I'd leave it in place just in case.
<?php
class upload
{
protected $user;
protected $isImage;
protected $isMovie;
protected $file;
protected $uploadFilename;
protected $uploadDirectory;
protected $fileSize;
protected $fileTmpName;
protected $fileType;
protected $fileExtension;
protected $saveFilePath;
protected $allowedExtensions;
function __construct($file, $userPointer)
{
// set the file we're uploading
$this->file = $file;
// if this is tied to a user, link the user account here
$this->user = $userPointer;
// set default bool values to false since we don't know what file type is being uploaded yet
$this->isImage = FALSE;
$this->isMovie = FALSE;
// setup file properties
if (isset($this->file) && !empty($this->file))
{
$this->uploadFilename = $this->file['file']['name'];
$this->fileSize = $this->file['file']['size'];
$this->fileTmpName = $this->file['file']['tmp_name'];
$this->fileType = $this->file['file']['type'];
}
else
{
throw new Exception('Received empty data. No file found to upload.');
}
// get the file extension of the file we're trying to upload
$tmp = explode('.', $this->uploadFilename);
$this->fileExtension = strtolower(end($tmp));
}
public function image($postParams)
{
// set default error alert (or whatever you want to return if error)
$retVal = array('alert' => '115');
// set our bool
$this->isImage = TRUE;
// set our type limits
$this->allowedExtensions = array("png");
// setup destination directory path (without filename yet)
$this->uploadDirectory = DIR_IMG_UPLOADS.$this->user->uid."/photos/";
// if user is not subscribed they are allowed only one image, clear their folder here
if ($this->user->isSubscribed() == FALSE)
{
$this->clearFolder($this->uploadDirectory);
}
// try to upload the file
$success = $this->startUpload();
if ($success === TRUE)
{
// return the image name (NOTE: this wipes the error alert set above)
$retVal = array(
'imageName' => $this->uploadFilename,
);
}
return $retVal;
}
public function movie($data)
{
// update php settings to handle larger uploads
set_time_limit(300);
// you may need to increase allowed filesize as well if your server is not set with a high enough limit
// set default return value (error code for upload failed)
$retVal = array('alert' => '92');
// set our bool
$this->isMovie = TRUE;
// set our allowed movie types
$this->allowedExtensions = array("mov", "mp4", "mpv", "3gp");
// setup destination path
$this->uploadDirectory = DIR_IMG_UPLOADS.$this->user->uid."/movies/";
// only upload the movie if the user is a subscriber
if ($this->user->isSubscribed())
{
// try to upload the file
$success = $this->startUpload();
if ($success === TRUE)
{
// file uploaded so set the new retval
$retVal = array('movieName' => $this->uploadFilename);
}
}
else
{
// return an error code so user knows this is a limited access feature
$retVal = array('alert' => '13');
}
return $retVal;
}
//-------------------------------------------------------------------------------
// Upload Process Methods
//-------------------------------------------------------------------------------
private function startUpload()
{
// see if there are any errors
$this->checkForUploadErrors();
// validate the type received is correct
$this->checkFileExtension();
// check the filesize
$this->checkFileSize();
// create the directory for the user if it does not exist
$this->createUserDirectoryIfNotExists();
// generate a local file name
$this->createLocalFileName();
// verify that the file is an uploaded file
$this->verifyIsUploadedFile();
// save the image to the appropriate folder
$success = $this->saveFileToDisk();
// return TRUE/FALSE
return $success;
}
private function checkForUploadErrors()
{
if ($this->file['file']['error'] != 0)
{
throw new Exception($this->file['file']['error']);
}
}
private function checkFileExtension()
{
if ($this->isImage)
{
// check if we are in fact uploading a png image, if not return error
if (!(in_array($this->fileExtension, $this->allowedExtensions)) || $this->fileType != 'image/png' || exif_imagetype($this->fileTmpName) != IMAGETYPE_PNG)
{
throw new Exception('Unsupported image type. The image must be of type png.');
}
}
else if ($this->isMovie)
{
// check if we are in fact uploading an accepted movie type
if (!(in_array($this->fileExtension, $this->allowedExtensions)) || $this->fileType != 'video/mov')
{
throw new Exception('Unsupported movie type. Accepted movie types are .mov, .mp4, .mpv, or .3gp');
}
}
}
private function checkFileSize()
{
if ($this->isImage)
{
if($this->fileSize > TenMB)
{
throw new Exception('The image filesize must be under 10MB.');
}
}
else if ($this->isMovie)
{
if($this->fileSize > TwentyFiveMB)
{
throw new Exception('The movie filesize must be under 25MB.');
}
}
}
private function createUserDirectoryIfNotExists()
{
if (!file_exists($this->uploadDirectory))
{
mkdir($this->uploadDirectory, 0755, true);
}
else
{
if ($this->isMovie)
{
// clear any prior uploads from the directory (only one movie file per user)
$this->clearFolder($this->uploadDirectory);
}
}
}
private function createLocalFileName()
{
$now = time();
// try to create a unique filename for this users file
while(file_exists($this->uploadFilename = $now.'-'.$this->uid.'.'.$this->fileExtension))
{
$now++;
}
// create our full file save path
$this->saveFilePath = $this->uploadDirectory.$this->uploadFilename;
}
private function clearFolder($path)
{
if(is_file($path))
{
// if there's already a file with this name clear it first
return #unlink($path);
}
elseif(is_dir($path))
{
// if it's a directory, clear it's contents
$scan = glob(rtrim($path,'/').'/*');
foreach($scan as $index=>$npath)
{
$this->clearFolder($npath);
#rmdir($npath);
}
}
}
private function verifyIsUploadedFile()
{
if (! is_uploaded_file($this->file['file']['tmp_name']))
{
throw new Exception('The file failed to upload.');
}
}
private function saveFileToDisk()
{
if (move_uploaded_file($this->file['file']['tmp_name'], $this->saveFilePath))
{
return TRUE;
}
throw new Exception('File failed to upload. Please retry.');
}
}
?>
Here's some sample code demonstrating how you might use the upload class...
// get a reference to your user object if applicable
$myUser = $this->someMethodThatFetchesUserWithId($myUserId);
// get reference to file to upload
$myFile = isset($_FILES) ? $_FILES : NULL;
// use try catch to return an error for any exceptions thrown in the upload script
try
{
// create and setup upload class
$upload = new upload($myFile, $myUser);
// trigger file upload
$data = $upload->image(); // if uploading an image
$data = $upload->movie(); // if uploading movie
// return any status messages as json string
echo json_encode($data);
}
catch (Exception $exception)
{
$retData = array(
'status' => 'FALSE',
'payload' => array(
'errorMsg' => $exception->getMessage()
),
);
echo json_encode($retData);
}
I have made a lightweight backup method for the Mobile-AppSales app available at github
I wrote about it here http://memention.com/blog/2009/11/22/Lightweight-backup.html
Look for the - (void)startUpload method in ReportManager.m
An update to #Brandon's answer, generalized to a method
- (NSString*) postToUrl:(NSString*)urlString data:(NSData*)dataToSend withFilename:(NSString*)filename
{
NSMutableURLRequest *request= [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#", boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
NSMutableData *postbody = [NSMutableData data];
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"userfile\"; filename=\"%#\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[#"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[NSData dataWithData:dataToSend]];
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postbody];
NSError* error;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
if (returnData) {
return [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
}
else {
return nil;
}
}
Invoke like so, sending data from a string:
[self postToUrl:#"<#Your url string#>"
data:[#"<#Your string to send#>" dataUsingEncoding:NSUTF8StringEncoding]
withFilename:#"<#Filename to post with#>"];