json - एक फ़ाइल और एसोसिएटेड डेटा को एक विश्वसनीय वेब सेवा में अधिमानतः जेएसओएन के रूप में पोस्ट करना




rest grails file-upload (9)

यह शायद एक बेवकूफ सवाल होने जा रहा है लेकिन मुझे उन रातों में से एक है। एक आवेदन में मैं रीस्टफुल एपीआई विकसित कर रहा हूं और हम चाहते हैं कि ग्राहक जेएसओएन के रूप में डेटा भेज सके। इस एप्लिकेशन के भाग के लिए क्लाइंट को फ़ाइल (आमतौर पर एक छवि) अपलोड करने के साथ-साथ छवि के बारे में जानकारी अपलोड करने की आवश्यकता होती है।

मुझे एक कठिन अनुरोध है कि यह एक अनुरोध में कैसे होता है। क्या फ़ाइल डेटा को JSON स्ट्रिंग में बेस 64 करना संभव है? क्या मुझे सर्वर पर 2 पोस्ट करने की आवश्यकता होगी? क्या मुझे इसके लिए JSON का उपयोग नहीं करना चाहिए?

एक साइड नोट के रूप में, हम बैकएंड पर Grails का उपयोग कर रहे हैं और इन सेवाओं को मूल मोबाइल क्लाइंट (आईफोन, एंड्रॉइड, आदि) द्वारा उपयोग किया जाता है, यदि इनमें से कोई भी एक फर्क पड़ता है।


Answers

मुझे पता है कि यह सवाल पुराना है, लेकिन आखिरी दिनों में मैंने इस वेब पर समाधान के लिए पूरे वेब की खोज की थी। मेरे पास रीस्ट वेब सर्विसेज और आईफोन क्लाइंट हैं जो चित्र, शीर्षक और विवरण भेजते हैं।

मुझे नहीं पता कि मेरा दृष्टिकोण सबसे अच्छा है, लेकिन इतना आसान और सरल है।

मैं UIImagePickerController का उपयोग करके एक तस्वीर लेता हूं और तस्वीर के डेटा भेजने के अनुरोध के हेडर टैग का उपयोग करके एनएसडीटा सर्वर को भेजता हूं।

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"myServerAddress"]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:UIImageJPEGRepresentation(picture, 0.5)];
[request setValue:@"image/jpeg" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"myPhotoTitle" forHTTPHeaderField:@"Photo-Title"];
[request setValue:@"myPhotoDescription" forHTTPHeaderField:@"Photo-Description"];

NSURLResponse *response;

NSError *error;

[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

सर्वर की तरफ, मुझे कोड का उपयोग कर फोटो प्राप्त होता है:

InputStream is = request.inputStream

def receivedPhotoFile = (IOUtils.toByteArray(is))

def photo = new Photo()
photo.photoFile = receivedPhotoFile //photoFile is a transient attribute
photo.title = request.getHeader("Photo-Title")
photo.description = request.getHeader("Photo-Description")
photo.imageURL = "temp"    

if (photo.save()) {    

    File saveLocation = grailsAttributes.getApplicationContext().getResource(File.separator + "images").getFile()
    saveLocation.mkdirs()

    File tempFile = File.createTempFile("photo", ".jpg", saveLocation)

    photo.imageURL = saveLocation.getName() + "/" + tempFile.getName()

    tempFile.append(photo.photoFile);

} else {

    println("Error")

}

मुझे नहीं पता कि मुझे भविष्य में समस्याएं हैं, लेकिन अब उत्पादन वातावरण में ठीक काम कर रहा है।


यदि आप एक आराम सर्वर विकसित कर रहे हैं तो आप यह कर सकते हैं

  1. क्या क्लाइंट ने HTTP पर फ़ाइल का पर्दाफाश किया है
  2. ग्राहक तब आपके {"file_url":"http://cockwombles.com/blah.jpg"} डेटा के साथ यूआरएल भेज सकता है जैसे छवि फ़ाइल {"file_url":"http://cockwombles.com/blah.jpg"}
  3. सर्वर तब फ़ाइल डाउनलोड कर सकता है।

मुझे पता है कि यह धागा काफी पुराना है, हालांकि, मैं यहां एक विकल्प याद कर रहा हूं। यदि आपके पास मेटाडेटा (किसी भी प्रारूप में) है जिसे आप अपलोड करने के लिए डेटा के साथ भेजना चाहते हैं, तो आप एक multipart/related अनुरोध कर सकते हैं।

मल्टीपार्ट / संबंधित मीडिया प्रकार कंपाउंड ऑब्जेक्ट्स के लिए है जो कई अंतर-संबंधित शरीर भागों से युक्त है।

आप अधिक गहराई से विवरण के लिए आरएफसी 2387 विनिर्देश की जांच कर सकते हैं।

मूल रूप से इस तरह के अनुरोध के प्रत्येक भाग में विभिन्न प्रकार के साथ सामग्री हो सकती है और सभी भागों किसी भी तरह से संबंधित होते हैं (उदाहरण के लिए एक छवि और यह मेटाडाटा)। भागों को एक स्ट्रिंग स्ट्रिंग द्वारा पहचाना जाता है, और अंतिम सीमा स्ट्रिंग के बाद दो हाइफ़न होते हैं।

उदाहरण:

POST /upload HTTP/1.1
Host: www.hostname.com
Content-Type: multipart/related; boundary=xyz
Content-Length: [actual-content-length]

--xyz
Content-Type: application/json; charset=UTF-8

{
    "name": "Sample image",
    "desc": "...",
    ...
}

--xyz
Content-Type: image/jpeg

[image data]
[image data]
[image data]
...
--foo_bar_baz--

आप multipart/form-data सामग्री प्रकार का उपयोग कर एक अनुरोध में फ़ाइल और डेटा भेज सकते हैं:

कई अनुप्रयोगों में, उपयोगकर्ता के लिए एक फॉर्म प्रस्तुत करना संभव है। उपयोगकर्ता टाइप की गई जानकारी, उपयोगकर्ता इनपुट द्वारा जेनरेट की गई जानकारी, या उपयोगकर्ता द्वारा चुनी गई फ़ाइलों से शामिल फॉर्म भर देगा। जब फॉर्म भर जाता है, तो फॉर्म से डेटा उपयोगकर्ता से प्राप्त करने वाले आवेदन में भेजा जाता है।

मल्टीपार्ट / फॉर्म-डेटा की परिभाषा उन अनुप्रयोगों में से एक से ली गई है ...

http://www.faqs.org/rfcs/rfc2388.html :

"मल्टीपार्ट / फॉर्म-डेटा" में भागों की श्रृंखला शामिल है। प्रत्येक भाग में एक सामग्री-स्वभाव शीर्षलेख [आरएफसी 2183] शामिल होने की उम्मीद है जहां स्वभाव प्रकार "फॉर्म-डेटा" है, और जहां स्वभाव में "नाम" का एक (अतिरिक्त) पैरामीटर होता है, जहां उस पैरामीटर का मान मूल है फॉर्म में फ़ील्ड नाम। उदाहरण के लिए, एक भाग में हेडर हो सकता है:

सामग्री-विस्थापन: फॉर्म-डेटा; नाम = "उपयोगकर्ता"

"उपयोगकर्ता" फ़ील्ड के प्रवेश से संबंधित मान के साथ।

आप सीमाओं के बीच प्रत्येक खंड के भीतर फ़ाइल जानकारी या फ़ील्ड जानकारी शामिल कर सकते हैं। मैंने सफलतापूर्वक एक ऐसी सेवा लागू की है जिसके लिए उपयोगकर्ता को डेटा और एक फॉर्म दोनों सबमिट करने की आवश्यकता है, और मल्टीपार्ट / फॉर्म-डेटा पूरी तरह से काम करता है। यह सेवा जावा / स्प्रिंग का उपयोग करके बनाई गई थी, और ग्राहक सी # का उपयोग कर रहा था, इसलिए दुर्भाग्यवश मेरे पास सेवा सेट अप करने के बारे में आपको देने के लिए कोई Grails उदाहरण नहीं हैं। आपको इस मामले में JSON का उपयोग करने की आवश्यकता नहीं है क्योंकि प्रत्येक "फॉर्म-डेटा" अनुभाग आपको पैरामीटर और उसके मान का नाम निर्दिष्ट करने के लिए एक स्थान प्रदान करता है।

मल्टीपार्ट / फॉर्म-डेटा का उपयोग करने के बारे में अच्छी बात यह है कि आप HTTP-डिफ़ाइंड हेडर का उपयोग कर रहे हैं, इसलिए आप अपनी सेवा बनाने के लिए मौजूदा HTTP टूल का उपयोग करने के आरईएसटी दर्शन के साथ चिपके हुए हैं।


फॉर्मडाटा ऑब्जेक्ट्स: अजाक्स का उपयोग कर फ़ाइलें अपलोड करें

XMLHttpRequest स्तर 2 नए फॉर्मडाटा इंटरफ़ेस के लिए समर्थन जोड़ता है। फॉर्मडाटा ऑब्जेक्ट्स फॉर्म फ़ील्ड और उनके मानों का प्रतिनिधित्व करने वाले कुंजी / मान जोड़े के सेट को आसानी से बनाने का एक तरीका प्रदान करते हैं, जिसे XMLHttpRequest send () विधि का उपयोग करके आसानी से भेजा जा सकता है।

function AjaxFileUpload() {
    var file = document.getElementById("files");
    //var file = fileInput;
    var fd = new FormData();
    fd.append("imageFileData", file);
    var xhr = new XMLHttpRequest();
    xhr.open("POST", '/ws/fileUpload.do');
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
             alert('success');
        }
        else if (uploadResult == 'success')
             alert('error');
    };
    xhr.send(fd);
}

https://developer.mozilla.org/en-US/docs/Web/API/FormData


यहां मेरा दृष्टिकोण API है (मैं उदाहरण का उपयोग करता हूं) - जैसा कि आप देख सकते हैं, आप API में किसी भी file_id (सर्वर में अपलोड की गई फ़ाइल पहचानकर्ता) का उपयोग नहीं करते हैं:

1. सर्वर पर 'फोटो' ऑब्जेक्ट बनाएं:

POST: /projects/{project_id}/photos   
params in: {name:some_schema.jpg, comment:blah}
return: photo_id

2. फ़ाइल अपलोड करें (ध्यान दें कि 'फ़ाइल' एकवचन रूप में है क्योंकि यह प्रति तस्वीर केवल एक है):

POST: /projects/{project_id}/photos/{photo_id}/file
params in: file to upload
return: -

और फिर उदाहरण के लिए:

3. फोटो सूची पढ़ें

GET: /projects/{project_id}/photos
params in: -
return: array of objects: [ photo, photo, photo, ... ]

4. कुछ फोटो विवरण पढ़ें

GET: /projects/{project_id}/photos/{photo_id}
params in: -
return: photo = { id: 666, name:'some_schema.jpg', comment:'blah'}

5. फोटो फ़ाइल पढ़ें

GET: /projects/{project_id}/photos/{photo_id}/file
params in: -
return: file content

तो निष्कर्ष यह है कि, सबसे पहले आप POST द्वारा ऑब्जेक्ट (फोटो) बनाते हैं, और फिर आप फ़ाइल के साथ सेकेंड अनुरोध भेजते हैं (फिर POST)।


चूंकि एकमात्र गायब उदाहरण एंड्रॉइड उदाहरण है , इसलिए मैं इसे जोड़ूंगा । यह तकनीक एक कस्टम AsyncTask का उपयोग करती है जिसे आपकी गतिविधि कक्षा के अंदर घोषित किया जाना चाहिए।

private class UploadFile extends AsyncTask<Void, Integer, String> {
    @Override
    protected void onPreExecute() {
        // set a status bar or show a dialog to the user here
        super.onPreExecute();
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        // progress[0] is the current status (e.g. 10%)
        // here you can update the user interface with the current status
    }

    @Override
    protected String doInBackground(Void... params) {
        return uploadFile();
    }

    private String uploadFile() {

        String responseString = null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost("http://example.com/upload-file");

        try {
            AndroidMultiPartEntity ampEntity = new AndroidMultiPartEntity(
                new ProgressListener() {
                    @Override
                        public void transferred(long num) {
                            // this trigger the progressUpdate event
                            publishProgress((int) ((num / (float) totalSize) * 100));
                        }
            });

            File myFile = new File("/my/image/path/example.jpg");

            ampEntity.addPart("fileFieldName", new FileBody(myFile));

            totalSize = ampEntity.getContentLength();
            httpPost.setEntity(ampEntity);

            // Making server call
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();

            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                responseString = EntityUtils.toString(httpEntity);
            } else {
                responseString = "Error, http status: "
                        + statusCode;
            }

        } catch (Exception e) {
            responseString = e.getMessage();
        }
        return responseString;
    }

    @Override
    protected void onPostExecute(String result) {
        // if you want update the user interface with upload result
        super.onPostExecute(result);
    }

}

इसलिए, जब आप अपनी फाइल अपलोड करना चाहते हैं तो बस कॉल करें:

new UploadFile().execute();

@RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST)
    public @ResponseBody Object jsongStrImage(@RequestParam(value="image") MultipartFile image, @RequestParam String jsonStr) {
-- use  com.fasterxml.jackson.databind.ObjectMapper convert Json String to Object
}

हम # 3 के निम्नलिखित संस्करण का उपयोग करते हैं: एक JSON- केवल REST API सर्वर बनाएं। एक HTML वेबसाइट सर्वर बनाओ। एचटीएमएल वेब सर्वर आपके संस्करण में नहीं है, एक क्लाइंट आरईएसटी एपीआई सर्वर के लिए है। इसके बजाय, दोनों साथी हैं। सतह से बहुत नीचे नहीं, एक आंतरिक एपीआई है जो दो सर्वरों की कार्यक्षमता प्रदान करती है।

हम किसी भी उदाहरण से अवगत नहीं हैं, इसलिए यह प्रयोगात्मक है। अब तक (बीटा में प्रवेश करने के बारे में), यह बहुत अच्छी तरह से काम किया है।





json rest grails file-upload